diff --git a/README.md b/README.md index b43e85e..b4edb92 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ 该翻译版本已获得原作者(Ivo Balbaert)本人授权,并表示支持开源事业的发展! ##翻译进度 -4.9 [指针](eBook/04.9.md) +第5章:[控制结构](eBook/05.0.md) ##支持本书 如果你喜欢本书《Go入门指南》,你可以参与到本书的翻译或纠正工作中来,具体请联系【无闻 E-mail:joe2010xtmf#163.com】,一同完善本书并帮助壮大 Go 语言在国内的学习群体,给大家提供更好的学习资源。 diff --git a/eBook/05.0.md b/eBook/05.0.md index 8bbdaa8..f50fc94 100644 --- a/eBook/05.0.md +++ b/eBook/05.0.md @@ -1,23 +1,8 @@ -##啊哦,亲,你看得也太快了。。。还没翻译完呢 0 0 -要不等到 ***2013 年 6 月 20 日*** 再来看看吧~~ - -这里还有一些其它的学习资源噢~ - - - [《Go编程基础》](https://github.com/Unknwon/go-fundamental-programming):已更新至 [第12课](https://github.com/Unknwon/go-fundamental-programming/blob/master/lectures/lecture12.md) - - [《Go Web编程》](https://github.com/astaxie/build-web-application-with-golang) - -神马?你说你不想学习?那好吧,去逛逛看看行情也行~ - -- [Go Walker](http://gowalker.org) **Go 项目文档在线浏览工具** -- [Golang中文社区](http://bbs.mygolang.com/forum.php) -- [Go语言学习园地](http://studygolang.com/) -- [Golang中国](http://golang.tc) - #5.0 控制结构 到目前为止,我们看到的都是 Go 程序都是从 main() 函数开始执行,然后按顺序执行该函数体中的代码。但我们经常会需要只有在满足一些特定情况时才执行某些代码,也就是说在代码里进行条件判断。针对这种需求,Go 提供了下面这些条件结构和分支结构: if-else 结构 - switch-else 结构 + switch 结构 select 结构,用于 channel 的选择(第 14.4 节) 可以使用迭代或循环结构来重复执行一次或多次某段代码(任务): diff --git a/eBook/05.1.md b/eBook/05.1.md index f0f015d..a071c10 100644 --- a/eBook/05.1.md +++ b/eBook/05.1.md @@ -1,3 +1,18 @@ +##啊哦,亲,你看得也太快了。。。还没翻译完呢 0 0 +要不等到 ***2013 年 6 月 24 日*** 再来看看吧~~ + +这里还有一些其它的学习资源噢~ + + - [《Go编程基础》](https://github.com/Unknwon/go-fundamental-programming):已更新至 [第12课](https://github.com/Unknwon/go-fundamental-programming/blob/master/lectures/lecture12.md) + - [《Go Web编程》](https://github.com/astaxie/build-web-application-with-golang) + +神马?你说你不想学习?那好吧,去逛逛看看行情也行~ + +- [Go Walker](http://gowalker.org) **Go 项目文档在线浏览工具** +- [Golang中文社区](http://bbs.mygolang.com/forum.php) +- [Go语言学习园地](http://studygolang.com/) +- [Golang中国](http://golang.tc) + #5.1 if-else 结构 if 是用于测试某个条件(布尔型或逻辑型)的语句,如果该条件成立,则会执行 if 后由大括号括起来的代码块,否则就忽略该代码块继续执行后续的代码。 @@ -189,4 +204,4 @@ if value := process(data); value > max { ##链接 - [目录](directory.md) - 上一节:[控制结构](05.0.md) -- 下一节: [TODO](05.2.md) +- 下一节: [测试多返回值函数的错误](05.2.md) diff --git a/eBook/directory.md b/eBook/directory.md index bbf23d2..d4d7118 100644 --- a/eBook/directory.md +++ b/eBook/directory.md @@ -38,6 +38,11 @@ - 4.9 [指针](04.9.md) - 第5章:[控制结构](05.0.md) - 5.1 [if-else 结构](05.1.md) + - 5.2 [测试多返回值函数的错误](05.2.md) + - 5.3 [switch 结构](05.3.md) + - 5.4 [for 结构](05.4.md) + - 5.5 [Break 与 continue](05.5.md) + - 5.6 [标签与 goto](05.6.md) - 第6章:函数(function) - 第7章:数组(array)与切片(slice) - 第8章:Maps diff --git a/eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language.htm b/eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language.htm deleted file mode 100644 index a54b9d5..0000000 --- a/eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language.htm +++ /dev/null @@ -1,1027 +0,0 @@ - - - - - Codelab: Writing Web Applications - The Go Programming Language - - - - - - -
-
-

The Go Programming Language

- - -
-
- - -

Codelab: Writing Web Applications

- - - - - - -

Introduction[Top]

- -

-Covered in this codelab: -

- - -

-Assumed knowledge: -

- - -

Getting Started[Top]

- -

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

- -

-Install Go (see the Installation Instructions). -

- -

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

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

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

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

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

- -

Data Structures[Top]

- -

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

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

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

- -

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

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

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

- -

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

- -

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

- -

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

- -

-We will want to load pages, too: -

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

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

- -

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

- -

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

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

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

- -

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

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

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

- -

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

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

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

- -

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

- -

Introducing the http package (an interlude)[Top]

- -

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

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

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

- -

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

- -

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

- -

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

- -

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

- -

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

-
http://localhost:8080/monkeys
-

-the program would present a page containing: -

-
Hi there, I love monkeys!
- -

Using http to serve wiki pages[Top]

- -

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

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

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

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

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

- -

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

- -

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

- -

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

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

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

- -

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

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

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

- -

Editing Pages[Top]

- -

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

- -

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

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

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

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

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

- -

The template package[Top]

- -

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

- -

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

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

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

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

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

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

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

- -

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

- -

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

- -

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

- -

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

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

-Modify viewHandler accordingly: -

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

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

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

-The handlers are now shorter and simpler. -

- -

Handling non-existent pages[Top]

- -

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

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

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

- -

Saving Pages[Top]

- -

-The function saveHandler will handle the form submission. -

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

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

- -

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

- -

Error handling[Top]

- -

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

- -

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

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

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

- -

-Now let's fix up saveHandler: -

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

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

- -

Template caching[Top]

- -

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

- -

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

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

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

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

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

- -

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

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

Validation[Top]

- -

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

- -

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

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

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

- -

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

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

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

- -

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

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

Introducing Function Literals and Closures[Top]

- -

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

- -

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

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

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

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

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

- -

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

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

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

- -

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

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

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

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

Try it out![Top]

- -

-Click here to view the final code listing. -

- -

-Recompile the code, and run the app: -

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

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

- -

Other tasks[Top]

- -

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

- - - -
-
-

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

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

This sweet header

. - * Takes a node, returns a string. - */ -function godocs_nodeToText(node) { - var TEXT_NODE = 3; // Defined in Mozilla but not MSIE :( - - var text = ''; - for (var j = 0; j != node.childNodes.length; j++) { - var child = node.childNodes[j]; - if (child.nodeType == TEXT_NODE) { - if (child.nodeValue != '[Top]') { //ok, that's a hack, but it works. - text = text + child.nodeValue; - } - } else { - text = text + godocs_nodeToText(child); - } - } - return text; -} - -/* For each H2 heading, add a link up to the #top of the document. - * (As part of this: ensure existence of 'top' named anchor link - * (theoretically at doc's top).) - */ -function godocs_addTopLinks() { - /* Make sure there's a "top" to link to. */ - var top = document.getElementById('top'); - if (!top) { - document.body.id = 'top'; - } - - if (!document.getElementsByTagName) return; // no browser support - - var headers = document.getElementsByTagName('h2'); - - for (var i = 0; i < headers.length; i++) { - var span = document.createElement('span'); - span.className = 'navtop'; - var link = document.createElement('a'); - span.appendChild(link); - link.href = '#top'; - var textNode = document.createTextNode('[Top]'); - link.appendChild(textNode); - headers[i].appendChild(span); - } -} diff --git a/eBook/examples/chapter_15/client.go b/eBook/examples/chapter_15/client.go deleted file mode 100644 index 9c6d861..0000000 --- a/eBook/examples/chapter_15/client.go +++ /dev/null @@ -1,36 +0,0 @@ -package main - -import ( - "fmt" - "os" - "net" - "bufio" - "strings" -) - -func main() { - conn, err := net.Dial("tcp", "localhost:50000") - if err != nil { - // No connection could be made because the target machine actively refused it. - fmt.Println("Error dialing", err.Error()) - return // terminate program - } - - inputReader := bufio.NewReader(os.Stdin) - fmt.Println("First, what is your name?") - clientName, _ := inputReader.ReadString('\n') - // fmt.Printf("CLIENTNAME %s",clientName) - trimmedClient := strings.Trim(clientName, "\r\n") // "\r\n" on Windows, "\n" on Linux - - for { - fmt.Println("What to send to the server? Type Q to quit.") - input, _ := inputReader.ReadString('\n') - trimmedInput := strings.Trim(input, "\r\n") - // fmt.Printf("input:--%s--",input) - // fmt.Printf("trimmedInput:--%s--",trimmedInput) - if trimmedInput == "Q" { - return - } - _, err = conn.Write([]byte(trimmedClient + " says: " + trimmedInput)) - } -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/00changelog.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/00changelog.i deleted file mode 100644 index d3a8311..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/00changelog.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/branch b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/branch deleted file mode 100644 index 4ad96d5..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/branch +++ /dev/null @@ -1 +0,0 @@ -default diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/cache/branchheads b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/cache/branchheads deleted file mode 100644 index 2844416..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/cache/branchheads +++ /dev/null @@ -1,2 +0,0 @@ -844fb91a777b63798d4657e1c40669e8968f79ad 4 -844fb91a777b63798d4657e1c40669e8968f79ad default diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/cache/tags b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/cache/tags deleted file mode 100644 index 25f9984..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/cache/tags +++ /dev/null @@ -1,2 +0,0 @@ -4 844fb91a777b63798d4657e1c40669e8968f79ad - diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/dirstate b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/dirstate deleted file mode 100644 index 3f0d5e6..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/dirstate and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/hgrc b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/hgrc deleted file mode 100644 index 692ebb4..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/hgrc +++ /dev/null @@ -1,2 +0,0 @@ -[paths] -default = https://code.google.com/p/go.net diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/requires b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/requires deleted file mode 100644 index ca69271..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/requires +++ /dev/null @@ -1,4 +0,0 @@ -revlogv1 -store -fncache -dotencode diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/00changelog.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/00changelog.i deleted file mode 100644 index aa4cdf8..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/00changelog.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/00manifest.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/00manifest.i deleted file mode 100644 index 9fb8a62..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/00manifest.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_a_u_t_h_o_r_s.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_a_u_t_h_o_r_s.i deleted file mode 100644 index c16b17e..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_a_u_t_h_o_r_s.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_c_o_n_t_r_i_b_u_t_o_r_s.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_c_o_n_t_r_i_b_u_t_o_r_s.i deleted file mode 100644 index b853cf0..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_c_o_n_t_r_i_b_u_t_o_r_s.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_l_i_c_e_n_s_e.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_l_i_c_e_n_s_e.i deleted file mode 100644 index aa52959..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_l_i_c_e_n_s_e.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_r_e_a_d_m_e.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_r_e_a_d_m_e.i deleted file mode 100644 index 75b7cc9..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_r_e_a_d_m_e.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/codereview.cfg.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/codereview.cfg.i deleted file mode 100644 index 833f42f..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/codereview.cfg.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/dict/dict.go.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/dict/dict.go.i deleted file mode 100644 index 402e254..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/dict/dict.go.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/read.go.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/read.go.i deleted file mode 100644 index 8f166d4..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/read.go.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/spdy__test.go.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/spdy__test.go.i deleted file mode 100644 index cfd8899..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/spdy__test.go.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/types.go.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/types.go.i deleted file mode 100644 index 35054ee..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/types.go.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/write.go.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/write.go.i deleted file mode 100644 index c81b7d5..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/write.go.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/client.go.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/client.go.i deleted file mode 100644 index 65d4320..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/client.go.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hixie.go.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hixie.go.i deleted file mode 100644 index d52174f..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hixie.go.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hixie__test.go.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hixie__test.go.i deleted file mode 100644 index baf9710..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hixie__test.go.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hybi.go.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hybi.go.i deleted file mode 100644 index 83339c2..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hybi.go.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hybi__test.go.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hybi__test.go.i deleted file mode 100644 index 5cdb6cf..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hybi__test.go.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/server.go.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/server.go.i deleted file mode 100644 index 702fc38..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/server.go.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/websocket.go.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/websocket.go.i deleted file mode 100644 index 3e421a6..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/websocket.go.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/websocket__test.go.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/websocket__test.go.i deleted file mode 100644 index 03bae85..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/websocket__test.go.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/~2ehgignore.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/~2ehgignore.i deleted file mode 100644 index 3654614..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/~2ehgignore.i and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/fncache b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/fncache deleted file mode 100644 index a0058a5..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/fncache +++ /dev/null @@ -1,19 +0,0 @@ -data/websocket/hybi.go.i -data/AUTHORS.i -data/CONTRIBUTORS.i -data/spdy/read.go.i -data/dict/dict.go.i -data/codereview.cfg.i -data/README.i -data/websocket/websocket_test.go.i -data/.hgignore.i -data/websocket/hixie.go.i -data/websocket/hixie_test.go.i -data/websocket/websocket.go.i -data/LICENSE.i -data/spdy/types.go.i -data/spdy/spdy_test.go.i -data/websocket/client.go.i -data/spdy/write.go.i -data/websocket/hybi_test.go.i -data/websocket/server.go.i diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/undo b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/undo deleted file mode 100644 index 93c2690..0000000 Binary files a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/undo and /dev/null differ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.bookmarks b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.bookmarks deleted file mode 100644 index e69de29..0000000 diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.branch b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.branch deleted file mode 100644 index 331d858..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.branch +++ /dev/null @@ -1 +0,0 @@ -default \ No newline at end of file diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.desc b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.desc deleted file mode 100644 index 48a1e39..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.desc +++ /dev/null @@ -1,3 +0,0 @@ -0 -pull -https://code.google.com/p/go.net diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.dirstate b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.dirstate deleted file mode 100644 index e69de29..0000000 diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hgignore b/eBook/examples/chapter_15/code.google.com/p/go.net/.hgignore deleted file mode 100644 index 571db5f..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/.hgignore +++ /dev/null @@ -1,2 +0,0 @@ -syntax:glob -last-change diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/AUTHORS b/eBook/examples/chapter_15/code.google.com/p/go.net/AUTHORS deleted file mode 100644 index 15167cd..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/AUTHORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code refers to The Go Authors for copyright purposes. -# The master list of authors is in the main Go distribution, -# visible at http://tip.golang.org/AUTHORS. diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/CONTRIBUTORS b/eBook/examples/chapter_15/code.google.com/p/go.net/CONTRIBUTORS deleted file mode 100644 index 1c4577e..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/CONTRIBUTORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code was written by the Go contributors. -# The master list of contributors is in the main Go distribution, -# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/LICENSE b/eBook/examples/chapter_15/code.google.com/p/go.net/LICENSE deleted file mode 100644 index 6a66aea..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/README b/eBook/examples/chapter_15/code.google.com/p/go.net/README deleted file mode 100644 index 6b13d8e..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/README +++ /dev/null @@ -1,3 +0,0 @@ -This repository holds supplementary Go networking libraries. - -To submit changes to this repository, see http://golang.org/doc/contribute.html. diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/codereview.cfg b/eBook/examples/chapter_15/code.google.com/p/go.net/codereview.cfg deleted file mode 100644 index e3eb47c..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/codereview.cfg +++ /dev/null @@ -1,2 +0,0 @@ -defaultcc: golang-dev@googlegroups.com -contributors: http://go.googlecode.com/hg/CONTRIBUTORS diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/dict/dict.go b/eBook/examples/chapter_15/code.google.com/p/go.net/dict/dict.go deleted file mode 100644 index e7f5290..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/dict/dict.go +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package dict implements the Dictionary Server Protocol -// as defined in RFC 2229. -package dict - -import ( - "net/textproto" - "strconv" - "strings" -) - -// A Client represents a client connection to a dictionary server. -type Client struct { - text *textproto.Conn -} - -// Dial returns a new client connected to a dictionary server at -// addr on the given network. -func Dial(network, addr string) (*Client, error) { - text, err := textproto.Dial(network, addr) - if err != nil { - return nil, err - } - _, _, err = text.ReadCodeLine(220) - if err != nil { - text.Close() - return nil, err - } - return &Client{text: text}, nil -} - -// Close closes the connection to the dictionary server. -func (c *Client) Close() error { - return c.text.Close() -} - -// A Dict represents a dictionary available on the server. -type Dict struct { - Name string // short name of dictionary - Desc string // long description -} - -// Dicts returns a list of the dictionaries available on the server. -func (c *Client) Dicts() ([]Dict, error) { - id, err := c.text.Cmd("SHOW DB") - if err != nil { - return nil, err - } - - c.text.StartResponse(id) - defer c.text.EndResponse(id) - - _, _, err = c.text.ReadCodeLine(110) - if err != nil { - return nil, err - } - lines, err := c.text.ReadDotLines() - if err != nil { - return nil, err - } - _, _, err = c.text.ReadCodeLine(250) - - dicts := make([]Dict, len(lines)) - for i := range dicts { - d := &dicts[i] - a, _ := fields(lines[i]) - if len(a) < 2 { - return nil, textproto.ProtocolError("invalid dictionary: " + lines[i]) - } - d.Name = a[0] - d.Desc = a[1] - } - return dicts, err -} - -// A Defn represents a definition. -type Defn struct { - Dict Dict // Dict where definition was found - Word string // Word being defined - Text []byte // Definition text, typically multiple lines -} - -// Define requests the definition of the given word. -// The argument dict names the dictionary to use, -// the Name field of a Dict returned by Dicts. -// -// The special dictionary name "*" means to look in all the -// server's dictionaries. -// The special dictionary name "!" means to look in all the -// server's dictionaries in turn, stopping after finding the word -// in one of them. -func (c *Client) Define(dict, word string) ([]*Defn, error) { - id, err := c.text.Cmd("DEFINE %s %q", dict, word) - if err != nil { - return nil, err - } - - c.text.StartResponse(id) - defer c.text.EndResponse(id) - - _, line, err := c.text.ReadCodeLine(150) - if err != nil { - return nil, err - } - a, _ := fields(line) - if len(a) < 1 { - return nil, textproto.ProtocolError("malformed response: " + line) - } - n, err := strconv.Atoi(a[0]) - if err != nil { - return nil, textproto.ProtocolError("invalid definition count: " + a[0]) - } - def := make([]*Defn, n) - for i := 0; i < n; i++ { - _, line, err = c.text.ReadCodeLine(151) - if err != nil { - return nil, err - } - a, _ := fields(line) - if len(a) < 3 { - // skip it, to keep protocol in sync - i-- - n-- - def = def[0:n] - continue - } - d := &Defn{Word: a[0], Dict: Dict{a[1], a[2]}} - d.Text, err = c.text.ReadDotBytes() - if err != nil { - return nil, err - } - def[i] = d - } - _, _, err = c.text.ReadCodeLine(250) - return def, err -} - -// Fields returns the fields in s. -// Fields are space separated unquoted words -// or quoted with single or double quote. -func fields(s string) ([]string, error) { - var v []string - i := 0 - for { - for i < len(s) && (s[i] == ' ' || s[i] == '\t') { - i++ - } - if i >= len(s) { - break - } - if s[i] == '"' || s[i] == '\'' { - q := s[i] - // quoted string - var j int - for j = i + 1; ; j++ { - if j >= len(s) { - return nil, textproto.ProtocolError("malformed quoted string") - } - if s[j] == '\\' { - j++ - continue - } - if s[j] == q { - j++ - break - } - } - v = append(v, unquote(s[i+1:j-1])) - i = j - } else { - // atom - var j int - for j = i; j < len(s); j++ { - if s[j] == ' ' || s[j] == '\t' || s[j] == '\\' || s[j] == '"' || s[j] == '\'' { - break - } - } - v = append(v, s[i:j]) - i = j - } - if i < len(s) { - c := s[i] - if c != ' ' && c != '\t' { - return nil, textproto.ProtocolError("quotes not on word boundaries") - } - } - } - return v, nil -} - -func unquote(s string) string { - if strings.Index(s, "\\") < 0 { - return s - } - b := []byte(s) - w := 0 - for r := 0; r < len(b); r++ { - c := b[r] - if c == '\\' { - r++ - c = b[r] - } - b[w] = c - w++ - } - return string(b[0:w]) -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/read.go b/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/read.go deleted file mode 100644 index 4830a1d..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/read.go +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package spdy - -import ( - "compress/zlib" - "encoding/binary" - "io" - "net/http" - "strings" -) - -func (frame *SynStreamFrame) read(h ControlFrameHeader, f *Framer) error { - return f.readSynStreamFrame(h, frame) -} - -func (frame *SynReplyFrame) read(h ControlFrameHeader, f *Framer) error { - return f.readSynReplyFrame(h, frame) -} - -func (frame *RstStreamFrame) read(h ControlFrameHeader, f *Framer) error { - frame.CFHeader = h - if err := binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { - return err - } - if err := binary.Read(f.r, binary.BigEndian, &frame.Status); err != nil { - return err - } - return nil -} - -func (frame *SettingsFrame) read(h ControlFrameHeader, f *Framer) error { - frame.CFHeader = h - var numSettings uint32 - if err := binary.Read(f.r, binary.BigEndian, &numSettings); err != nil { - return err - } - frame.FlagIdValues = make([]SettingsFlagIdValue, numSettings) - for i := uint32(0); i < numSettings; i++ { - if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Id); err != nil { - return err - } - frame.FlagIdValues[i].Flag = SettingsFlag((frame.FlagIdValues[i].Id & 0xff000000) >> 24) - frame.FlagIdValues[i].Id &= 0xffffff - if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Value); err != nil { - return err - } - } - return nil -} - -func (frame *NoopFrame) read(h ControlFrameHeader, f *Framer) error { - frame.CFHeader = h - return nil -} - -func (frame *PingFrame) read(h ControlFrameHeader, f *Framer) error { - frame.CFHeader = h - if err := binary.Read(f.r, binary.BigEndian, &frame.Id); err != nil { - return err - } - return nil -} - -func (frame *GoAwayFrame) read(h ControlFrameHeader, f *Framer) error { - frame.CFHeader = h - if err := binary.Read(f.r, binary.BigEndian, &frame.LastGoodStreamId); err != nil { - return err - } - return nil -} - -func (frame *HeadersFrame) read(h ControlFrameHeader, f *Framer) error { - return f.readHeadersFrame(h, frame) -} - -func newControlFrame(frameType ControlFrameType) (controlFrame, error) { - ctor, ok := cframeCtor[frameType] - if !ok { - return nil, &Error{Err: InvalidControlFrame} - } - return ctor(), nil -} - -var cframeCtor = map[ControlFrameType]func() controlFrame{ - TypeSynStream: func() controlFrame { return new(SynStreamFrame) }, - TypeSynReply: func() controlFrame { return new(SynReplyFrame) }, - TypeRstStream: func() controlFrame { return new(RstStreamFrame) }, - TypeSettings: func() controlFrame { return new(SettingsFrame) }, - TypeNoop: func() controlFrame { return new(NoopFrame) }, - TypePing: func() controlFrame { return new(PingFrame) }, - TypeGoAway: func() controlFrame { return new(GoAwayFrame) }, - TypeHeaders: func() controlFrame { return new(HeadersFrame) }, - // TODO(willchan): Add TypeWindowUpdate -} - -func (f *Framer) uncorkHeaderDecompressor(payloadSize int64) error { - if f.headerDecompressor != nil { - f.headerReader.N = payloadSize - return nil - } - f.headerReader = io.LimitedReader{R: f.r, N: payloadSize} - decompressor, err := zlib.NewReaderDict(&f.headerReader, []byte(HeaderDictionary)) - if err != nil { - return err - } - f.headerDecompressor = decompressor - return nil -} - -// ReadFrame reads SPDY encoded data and returns a decompressed Frame. -func (f *Framer) ReadFrame() (Frame, error) { - var firstWord uint32 - if err := binary.Read(f.r, binary.BigEndian, &firstWord); err != nil { - return nil, err - } - if (firstWord & 0x80000000) != 0 { - frameType := ControlFrameType(firstWord & 0xffff) - version := uint16(0x7fff & (firstWord >> 16)) - return f.parseControlFrame(version, frameType) - } - return f.parseDataFrame(firstWord & 0x7fffffff) -} - -func (f *Framer) parseControlFrame(version uint16, frameType ControlFrameType) (Frame, error) { - var length uint32 - if err := binary.Read(f.r, binary.BigEndian, &length); err != nil { - return nil, err - } - flags := ControlFlags((length & 0xff000000) >> 24) - length &= 0xffffff - header := ControlFrameHeader{version, frameType, flags, length} - cframe, err := newControlFrame(frameType) - if err != nil { - return nil, err - } - if err = cframe.read(header, f); err != nil { - return nil, err - } - return cframe, nil -} - -func parseHeaderValueBlock(r io.Reader, streamId uint32) (http.Header, error) { - var numHeaders uint16 - if err := binary.Read(r, binary.BigEndian, &numHeaders); err != nil { - return nil, err - } - var e error - h := make(http.Header, int(numHeaders)) - for i := 0; i < int(numHeaders); i++ { - var length uint16 - if err := binary.Read(r, binary.BigEndian, &length); err != nil { - return nil, err - } - nameBytes := make([]byte, length) - if _, err := io.ReadFull(r, nameBytes); err != nil { - return nil, err - } - name := string(nameBytes) - if name != strings.ToLower(name) { - e = &Error{UnlowercasedHeaderName, streamId} - name = strings.ToLower(name) - } - if h[name] != nil { - e = &Error{DuplicateHeaders, streamId} - } - if err := binary.Read(r, binary.BigEndian, &length); err != nil { - return nil, err - } - value := make([]byte, length) - if _, err := io.ReadFull(r, value); err != nil { - return nil, err - } - valueList := strings.Split(string(value), "\x00") - for _, v := range valueList { - h.Add(name, v) - } - } - if e != nil { - return h, e - } - return h, nil -} - -func (f *Framer) readSynStreamFrame(h ControlFrameHeader, frame *SynStreamFrame) error { - frame.CFHeader = h - var err error - if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { - return err - } - if err = binary.Read(f.r, binary.BigEndian, &frame.AssociatedToStreamId); err != nil { - return err - } - if err = binary.Read(f.r, binary.BigEndian, &frame.Priority); err != nil { - return err - } - frame.Priority >>= 14 - - reader := f.r - if !f.headerCompressionDisabled { - f.uncorkHeaderDecompressor(int64(h.length - 10)) - reader = f.headerDecompressor - } - - frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) - if !f.headerCompressionDisabled && ((err == io.EOF && f.headerReader.N == 0) || f.headerReader.N != 0) { - err = &Error{WrongCompressedPayloadSize, 0} - } - if err != nil { - return err - } - // Remove this condition when we bump Version to 3. - if Version >= 3 { - for h := range frame.Headers { - if invalidReqHeaders[h] { - return &Error{InvalidHeaderPresent, frame.StreamId} - } - } - } - return nil -} - -func (f *Framer) readSynReplyFrame(h ControlFrameHeader, frame *SynReplyFrame) error { - frame.CFHeader = h - var err error - if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { - return err - } - var unused uint16 - if err = binary.Read(f.r, binary.BigEndian, &unused); err != nil { - return err - } - reader := f.r - if !f.headerCompressionDisabled { - f.uncorkHeaderDecompressor(int64(h.length - 6)) - reader = f.headerDecompressor - } - frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) - if !f.headerCompressionDisabled && ((err == io.EOF && f.headerReader.N == 0) || f.headerReader.N != 0) { - err = &Error{WrongCompressedPayloadSize, 0} - } - if err != nil { - return err - } - // Remove this condition when we bump Version to 3. - if Version >= 3 { - for h := range frame.Headers { - if invalidRespHeaders[h] { - return &Error{InvalidHeaderPresent, frame.StreamId} - } - } - } - return nil -} - -func (f *Framer) readHeadersFrame(h ControlFrameHeader, frame *HeadersFrame) error { - frame.CFHeader = h - var err error - if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { - return err - } - var unused uint16 - if err = binary.Read(f.r, binary.BigEndian, &unused); err != nil { - return err - } - reader := f.r - if !f.headerCompressionDisabled { - f.uncorkHeaderDecompressor(int64(h.length - 6)) - reader = f.headerDecompressor - } - frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) - if !f.headerCompressionDisabled && ((err == io.EOF && f.headerReader.N == 0) || f.headerReader.N != 0) { - err = &Error{WrongCompressedPayloadSize, 0} - } - if err != nil { - return err - } - - // Remove this condition when we bump Version to 3. - if Version >= 3 { - var invalidHeaders map[string]bool - if frame.StreamId%2 == 0 { - invalidHeaders = invalidReqHeaders - } else { - invalidHeaders = invalidRespHeaders - } - for h := range frame.Headers { - if invalidHeaders[h] { - return &Error{InvalidHeaderPresent, frame.StreamId} - } - } - } - return nil -} - -func (f *Framer) parseDataFrame(streamId uint32) (*DataFrame, error) { - var length uint32 - if err := binary.Read(f.r, binary.BigEndian, &length); err != nil { - return nil, err - } - var frame DataFrame - frame.StreamId = streamId - frame.Flags = DataFlags(length >> 24) - length &= 0xffffff - frame.Data = make([]byte, length) - if _, err := io.ReadFull(f.r, frame.Data); err != nil { - return nil, err - } - return &frame, nil -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/spdy_test.go b/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/spdy_test.go deleted file mode 100644 index c1cad4b..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/spdy_test.go +++ /dev/null @@ -1,497 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package spdy - -import ( - "bytes" - "io" - "net/http" - "reflect" - "testing" -) - -func TestHeaderParsing(t *testing.T) { - headers := http.Header{ - "Url": []string{"http://www.google.com/"}, - "Method": []string{"get"}, - "Version": []string{"http/1.1"}, - } - var headerValueBlockBuf bytes.Buffer - writeHeaderValueBlock(&headerValueBlockBuf, headers) - - const bogusStreamId = 1 - newHeaders, err := parseHeaderValueBlock(&headerValueBlockBuf, bogusStreamId) - if err != nil { - t.Fatal("parseHeaderValueBlock:", err) - } - - if !reflect.DeepEqual(headers, newHeaders) { - t.Fatal("got: ", newHeaders, "\nwant: ", headers) - } -} - -func TestCreateParseSynStreamFrame(t *testing.T) { - buffer := new(bytes.Buffer) - framer := &Framer{ - headerCompressionDisabled: true, - w: buffer, - headerBuf: new(bytes.Buffer), - r: buffer, - } - synStreamFrame := SynStreamFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypeSynStream, - }, - Headers: http.Header{ - "Url": []string{"http://www.google.com/"}, - "Method": []string{"get"}, - "Version": []string{"http/1.1"}, - }, - } - if err := framer.WriteFrame(&synStreamFrame); err != nil { - t.Fatal("WriteFrame without compression:", err) - } - frame, err := framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame without compression:", err) - } - parsedSynStreamFrame, ok := frame.(*SynStreamFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { - t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) - } - - // Test again with compression - buffer.Reset() - framer, err = NewFramer(buffer, buffer) - if err != nil { - t.Fatal("Failed to create new framer:", err) - } - if err := framer.WriteFrame(&synStreamFrame); err != nil { - t.Fatal("WriteFrame with compression:", err) - } - frame, err = framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame with compression:", err) - } - parsedSynStreamFrame, ok = frame.(*SynStreamFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { - t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) - } -} - -func TestCreateParseSynReplyFrame(t *testing.T) { - buffer := new(bytes.Buffer) - framer := &Framer{ - headerCompressionDisabled: true, - w: buffer, - headerBuf: new(bytes.Buffer), - r: buffer, - } - synReplyFrame := SynReplyFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypeSynReply, - }, - Headers: http.Header{ - "Url": []string{"http://www.google.com/"}, - "Method": []string{"get"}, - "Version": []string{"http/1.1"}, - }, - } - if err := framer.WriteFrame(&synReplyFrame); err != nil { - t.Fatal("WriteFrame without compression:", err) - } - frame, err := framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame without compression:", err) - } - parsedSynReplyFrame, ok := frame.(*SynReplyFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(synReplyFrame, *parsedSynReplyFrame) { - t.Fatal("got: ", *parsedSynReplyFrame, "\nwant: ", synReplyFrame) - } - - // Test again with compression - buffer.Reset() - framer, err = NewFramer(buffer, buffer) - if err != nil { - t.Fatal("Failed to create new framer:", err) - } - if err := framer.WriteFrame(&synReplyFrame); err != nil { - t.Fatal("WriteFrame with compression:", err) - } - frame, err = framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame with compression:", err) - } - parsedSynReplyFrame, ok = frame.(*SynReplyFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(synReplyFrame, *parsedSynReplyFrame) { - t.Fatal("got: ", *parsedSynReplyFrame, "\nwant: ", synReplyFrame) - } -} - -func TestCreateParseRstStream(t *testing.T) { - buffer := new(bytes.Buffer) - framer, err := NewFramer(buffer, buffer) - if err != nil { - t.Fatal("Failed to create new framer:", err) - } - rstStreamFrame := RstStreamFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypeRstStream, - }, - StreamId: 1, - Status: InvalidStream, - } - if err := framer.WriteFrame(&rstStreamFrame); err != nil { - t.Fatal("WriteFrame:", err) - } - frame, err := framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame:", err) - } - parsedRstStreamFrame, ok := frame.(*RstStreamFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(rstStreamFrame, *parsedRstStreamFrame) { - t.Fatal("got: ", *parsedRstStreamFrame, "\nwant: ", rstStreamFrame) - } -} - -func TestCreateParseSettings(t *testing.T) { - buffer := new(bytes.Buffer) - framer, err := NewFramer(buffer, buffer) - if err != nil { - t.Fatal("Failed to create new framer:", err) - } - settingsFrame := SettingsFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypeSettings, - }, - FlagIdValues: []SettingsFlagIdValue{ - {FlagSettingsPersistValue, SettingsCurrentCwnd, 10}, - {FlagSettingsPersisted, SettingsUploadBandwidth, 1}, - }, - } - if err := framer.WriteFrame(&settingsFrame); err != nil { - t.Fatal("WriteFrame:", err) - } - frame, err := framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame:", err) - } - parsedSettingsFrame, ok := frame.(*SettingsFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(settingsFrame, *parsedSettingsFrame) { - t.Fatal("got: ", *parsedSettingsFrame, "\nwant: ", settingsFrame) - } -} - -func TestCreateParseNoop(t *testing.T) { - buffer := new(bytes.Buffer) - framer, err := NewFramer(buffer, buffer) - if err != nil { - t.Fatal("Failed to create new framer:", err) - } - noopFrame := NoopFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypeNoop, - }, - } - if err := framer.WriteFrame(&noopFrame); err != nil { - t.Fatal("WriteFrame:", err) - } - frame, err := framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame:", err) - } - parsedNoopFrame, ok := frame.(*NoopFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(noopFrame, *parsedNoopFrame) { - t.Fatal("got: ", *parsedNoopFrame, "\nwant: ", noopFrame) - } -} - -func TestCreateParsePing(t *testing.T) { - buffer := new(bytes.Buffer) - framer, err := NewFramer(buffer, buffer) - if err != nil { - t.Fatal("Failed to create new framer:", err) - } - pingFrame := PingFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypePing, - }, - Id: 31337, - } - if err := framer.WriteFrame(&pingFrame); err != nil { - t.Fatal("WriteFrame:", err) - } - frame, err := framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame:", err) - } - parsedPingFrame, ok := frame.(*PingFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(pingFrame, *parsedPingFrame) { - t.Fatal("got: ", *parsedPingFrame, "\nwant: ", pingFrame) - } -} - -func TestCreateParseGoAway(t *testing.T) { - buffer := new(bytes.Buffer) - framer, err := NewFramer(buffer, buffer) - if err != nil { - t.Fatal("Failed to create new framer:", err) - } - goAwayFrame := GoAwayFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypeGoAway, - }, - LastGoodStreamId: 31337, - } - if err := framer.WriteFrame(&goAwayFrame); err != nil { - t.Fatal("WriteFrame:", err) - } - frame, err := framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame:", err) - } - parsedGoAwayFrame, ok := frame.(*GoAwayFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(goAwayFrame, *parsedGoAwayFrame) { - t.Fatal("got: ", *parsedGoAwayFrame, "\nwant: ", goAwayFrame) - } -} - -func TestCreateParseHeadersFrame(t *testing.T) { - buffer := new(bytes.Buffer) - framer := &Framer{ - headerCompressionDisabled: true, - w: buffer, - headerBuf: new(bytes.Buffer), - r: buffer, - } - headersFrame := HeadersFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypeHeaders, - }, - } - headersFrame.Headers = http.Header{ - "Url": []string{"http://www.google.com/"}, - "Method": []string{"get"}, - "Version": []string{"http/1.1"}, - } - if err := framer.WriteFrame(&headersFrame); err != nil { - t.Fatal("WriteFrame without compression:", err) - } - frame, err := framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame without compression:", err) - } - parsedHeadersFrame, ok := frame.(*HeadersFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { - t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) - } - - // Test again with compression - buffer.Reset() - framer, err = NewFramer(buffer, buffer) - if err := framer.WriteFrame(&headersFrame); err != nil { - t.Fatal("WriteFrame with compression:", err) - } - frame, err = framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame with compression:", err) - } - parsedHeadersFrame, ok = frame.(*HeadersFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { - t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) - } -} - -func TestCreateParseDataFrame(t *testing.T) { - buffer := new(bytes.Buffer) - framer, err := NewFramer(buffer, buffer) - if err != nil { - t.Fatal("Failed to create new framer:", err) - } - dataFrame := DataFrame{ - StreamId: 1, - Data: []byte{'h', 'e', 'l', 'l', 'o'}, - } - if err := framer.WriteFrame(&dataFrame); err != nil { - t.Fatal("WriteFrame:", err) - } - frame, err := framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame:", err) - } - parsedDataFrame, ok := frame.(*DataFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(dataFrame, *parsedDataFrame) { - t.Fatal("got: ", *parsedDataFrame, "\nwant: ", dataFrame) - } -} - -func TestCompressionContextAcrossFrames(t *testing.T) { - buffer := new(bytes.Buffer) - framer, err := NewFramer(buffer, buffer) - if err != nil { - t.Fatal("Failed to create new framer:", err) - } - headersFrame := HeadersFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypeHeaders, - }, - Headers: http.Header{ - "Url": []string{"http://www.google.com/"}, - "Method": []string{"get"}, - "Version": []string{"http/1.1"}, - }, - } - if err := framer.WriteFrame(&headersFrame); err != nil { - t.Fatal("WriteFrame (HEADERS):", err) - } - synStreamFrame := SynStreamFrame{ControlFrameHeader{Version, TypeSynStream, 0, 0}, 0, 0, 0, nil} - synStreamFrame.Headers = http.Header{ - "Url": []string{"http://www.google.com/"}, - "Method": []string{"get"}, - "Version": []string{"http/1.1"}, - } - if err := framer.WriteFrame(&synStreamFrame); err != nil { - t.Fatal("WriteFrame (SYN_STREAM):", err) - } - frame, err := framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame (HEADERS):", err, buffer.Bytes()) - } - parsedHeadersFrame, ok := frame.(*HeadersFrame) - if !ok { - t.Fatalf("expected HeadersFrame; got %T %v", frame, frame) - } - if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { - t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) - } - frame, err = framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame (SYN_STREAM):", err, buffer.Bytes()) - } - parsedSynStreamFrame, ok := frame.(*SynStreamFrame) - if !ok { - t.Fatalf("expected SynStreamFrame; got %T %v", frame, frame) - } - if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { - t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) - } -} - -func TestMultipleSPDYFrames(t *testing.T) { - // Initialize the framers. - pr1, pw1 := io.Pipe() - pr2, pw2 := io.Pipe() - writer, err := NewFramer(pw1, pr2) - if err != nil { - t.Fatal("Failed to create writer:", err) - } - reader, err := NewFramer(pw2, pr1) - if err != nil { - t.Fatal("Failed to create reader:", err) - } - - // Set up the frames we're actually transferring. - headersFrame := HeadersFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypeHeaders, - }, - Headers: http.Header{ - "Url": []string{"http://www.google.com/"}, - "Method": []string{"get"}, - "Version": []string{"http/1.1"}, - }, - } - synStreamFrame := SynStreamFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypeSynStream, - }, - Headers: http.Header{ - "Url": []string{"http://www.google.com/"}, - "Method": []string{"get"}, - "Version": []string{"http/1.1"}, - }, - } - - // Start the goroutines to write the frames. - go func() { - if err := writer.WriteFrame(&headersFrame); err != nil { - t.Fatal("WriteFrame (HEADERS): ", err) - } - if err := writer.WriteFrame(&synStreamFrame); err != nil { - t.Fatal("WriteFrame (SYN_STREAM): ", err) - } - }() - - // Read the frames and verify they look as expected. - frame, err := reader.ReadFrame() - if err != nil { - t.Fatal("ReadFrame (HEADERS): ", err) - } - parsedHeadersFrame, ok := frame.(*HeadersFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { - t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) - } - frame, err = reader.ReadFrame() - if err != nil { - t.Fatal("ReadFrame (SYN_STREAM):", err) - } - parsedSynStreamFrame, ok := frame.(*SynStreamFrame) - if !ok { - t.Fatal("Parsed incorrect frame type.") - } - if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { - t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) - } -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/types.go b/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/types.go deleted file mode 100644 index 7c57d36..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/types.go +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package spdy - -import ( - "bytes" - "compress/zlib" - "io" - "net/http" -) - -// Data Frame Format -// +----------------------------------+ -// |0| Stream-ID (31bits) | -// +----------------------------------+ -// | flags (8) | Length (24 bits) | -// +----------------------------------+ -// | Data | -// +----------------------------------+ -// -// Control Frame Format -// +----------------------------------+ -// |1| Version(15bits) | Type(16bits) | -// +----------------------------------+ -// | flags (8) | Length (24 bits) | -// +----------------------------------+ -// | Data | -// +----------------------------------+ -// -// Control Frame: SYN_STREAM -// +----------------------------------+ -// |1|000000000000001|0000000000000001| -// +----------------------------------+ -// | flags (8) | Length (24 bits) | >= 12 -// +----------------------------------+ -// |X| Stream-ID(31bits) | -// +----------------------------------+ -// |X|Associated-To-Stream-ID (31bits)| -// +----------------------------------+ -// |Pri| unused | Length (16bits)| -// +----------------------------------+ -// -// Control Frame: SYN_REPLY -// +----------------------------------+ -// |1|000000000000001|0000000000000010| -// +----------------------------------+ -// | flags (8) | Length (24 bits) | >= 8 -// +----------------------------------+ -// |X| Stream-ID(31bits) | -// +----------------------------------+ -// | unused (16 bits)| Length (16bits)| -// +----------------------------------+ -// -// Control Frame: RST_STREAM -// +----------------------------------+ -// |1|000000000000001|0000000000000011| -// +----------------------------------+ -// | flags (8) | Length (24 bits) | >= 4 -// +----------------------------------+ -// |X| Stream-ID(31bits) | -// +----------------------------------+ -// | Status code (32 bits) | -// +----------------------------------+ -// -// Control Frame: SETTINGS -// +----------------------------------+ -// |1|000000000000001|0000000000000100| -// +----------------------------------+ -// | flags (8) | Length (24 bits) | -// +----------------------------------+ -// | # of entries (32) | -// +----------------------------------+ -// -// Control Frame: NOOP -// +----------------------------------+ -// |1|000000000000001|0000000000000101| -// +----------------------------------+ -// | flags (8) | Length (24 bits) | = 0 -// +----------------------------------+ -// -// Control Frame: PING -// +----------------------------------+ -// |1|000000000000001|0000000000000110| -// +----------------------------------+ -// | flags (8) | Length (24 bits) | = 4 -// +----------------------------------+ -// | Unique id (32 bits) | -// +----------------------------------+ -// -// Control Frame: GOAWAY -// +----------------------------------+ -// |1|000000000000001|0000000000000111| -// +----------------------------------+ -// | flags (8) | Length (24 bits) | = 4 -// +----------------------------------+ -// |X| Last-accepted-stream-id | -// +----------------------------------+ -// -// Control Frame: HEADERS -// +----------------------------------+ -// |1|000000000000001|0000000000001000| -// +----------------------------------+ -// | flags (8) | Length (24 bits) | >= 8 -// +----------------------------------+ -// |X| Stream-ID (31 bits) | -// +----------------------------------+ -// | unused (16 bits)| Length (16bits)| -// +----------------------------------+ -// -// Control Frame: WINDOW_UPDATE -// +----------------------------------+ -// |1|000000000000001|0000000000001001| -// +----------------------------------+ -// | flags (8) | Length (24 bits) | = 8 -// +----------------------------------+ -// |X| Stream-ID (31 bits) | -// +----------------------------------+ -// | Delta-Window-Size (32 bits) | -// +----------------------------------+ - -// Version is the protocol version number that this package implements. -const Version = 2 - -// ControlFrameType stores the type field in a control frame header. -type ControlFrameType uint16 - -// Control frame type constants -const ( - TypeSynStream ControlFrameType = 0x0001 - TypeSynReply = 0x0002 - TypeRstStream = 0x0003 - TypeSettings = 0x0004 - TypeNoop = 0x0005 - TypePing = 0x0006 - TypeGoAway = 0x0007 - TypeHeaders = 0x0008 - TypeWindowUpdate = 0x0009 -) - -// ControlFlags are the flags that can be set on a control frame. -type ControlFlags uint8 - -const ( - ControlFlagFin ControlFlags = 0x01 -) - -// DataFlags are the flags that can be set on a data frame. -type DataFlags uint8 - -const ( - DataFlagFin DataFlags = 0x01 - DataFlagCompressed = 0x02 -) - -// MaxDataLength is the maximum number of bytes that can be stored in one frame. -const MaxDataLength = 1<<24 - 1 - -// Frame is a single SPDY frame in its unpacked in-memory representation. Use -// Framer to read and write it. -type Frame interface { - write(f *Framer) error -} - -// ControlFrameHeader contains all the fields in a control frame header, -// in its unpacked in-memory representation. -type ControlFrameHeader struct { - // Note, high bit is the "Control" bit. - version uint16 - frameType ControlFrameType - Flags ControlFlags - length uint32 -} - -type controlFrame interface { - Frame - read(h ControlFrameHeader, f *Framer) error -} - -// SynStreamFrame is the unpacked, in-memory representation of a SYN_STREAM -// frame. -type SynStreamFrame struct { - CFHeader ControlFrameHeader - StreamId uint32 - AssociatedToStreamId uint32 - // Note, only 2 highest bits currently used - // Rest of Priority is unused. - Priority uint16 - Headers http.Header -} - -// SynReplyFrame is the unpacked, in-memory representation of a SYN_REPLY frame. -type SynReplyFrame struct { - CFHeader ControlFrameHeader - StreamId uint32 - Headers http.Header -} - -// StatusCode represents the status that led to a RST_STREAM -type StatusCode uint32 - -const ( - ProtocolError StatusCode = 1 - InvalidStream = 2 - RefusedStream = 3 - UnsupportedVersion = 4 - Cancel = 5 - InternalError = 6 - FlowControlError = 7 -) - -// RstStreamFrame is the unpacked, in-memory representation of a RST_STREAM -// frame. -type RstStreamFrame struct { - CFHeader ControlFrameHeader - StreamId uint32 - Status StatusCode -} - -// SettingsFlag represents a flag in a SETTINGS frame. -type SettingsFlag uint8 - -const ( - FlagSettingsPersistValue SettingsFlag = 0x1 - FlagSettingsPersisted = 0x2 -) - -// SettingsFlag represents the id of an id/value pair in a SETTINGS frame. -type SettingsId uint32 - -const ( - SettingsUploadBandwidth SettingsId = 1 - SettingsDownloadBandwidth = 2 - SettingsRoundTripTime = 3 - SettingsMaxConcurrentStreams = 4 - SettingsCurrentCwnd = 5 -) - -// SettingsFlagIdValue is the unpacked, in-memory representation of the -// combined flag/id/value for a setting in a SETTINGS frame. -type SettingsFlagIdValue struct { - Flag SettingsFlag - Id SettingsId - Value uint32 -} - -// SettingsFrame is the unpacked, in-memory representation of a SPDY -// SETTINGS frame. -type SettingsFrame struct { - CFHeader ControlFrameHeader - FlagIdValues []SettingsFlagIdValue -} - -// NoopFrame is the unpacked, in-memory representation of a NOOP frame. -type NoopFrame struct { - CFHeader ControlFrameHeader -} - -// PingFrame is the unpacked, in-memory representation of a PING frame. -type PingFrame struct { - CFHeader ControlFrameHeader - Id uint32 -} - -// GoAwayFrame is the unpacked, in-memory representation of a GOAWAY frame. -type GoAwayFrame struct { - CFHeader ControlFrameHeader - LastGoodStreamId uint32 -} - -// HeadersFrame is the unpacked, in-memory representation of a HEADERS frame. -type HeadersFrame struct { - CFHeader ControlFrameHeader - StreamId uint32 - Headers http.Header -} - -// DataFrame is the unpacked, in-memory representation of a DATA frame. -type DataFrame struct { - // Note, high bit is the "Control" bit. Should be 0 for data frames. - StreamId uint32 - Flags DataFlags - Data []byte -} - -// HeaderDictionary is the dictionary sent to the zlib compressor/decompressor. -// Even though the specification states there is no null byte at the end, Chrome sends it. -const HeaderDictionary = "optionsgetheadpostputdeletetrace" + - "acceptaccept-charsetaccept-encodingaccept-languageauthorizationexpectfromhost" + - "if-modified-sinceif-matchif-none-matchif-rangeif-unmodifiedsince" + - "max-forwardsproxy-authorizationrangerefererteuser-agent" + - "100101200201202203204205206300301302303304305306307400401402403404405406407408409410411412413414415416417500501502503504505" + - "accept-rangesageetaglocationproxy-authenticatepublicretry-after" + - "servervarywarningwww-authenticateallowcontent-basecontent-encodingcache-control" + - "connectiondatetrailertransfer-encodingupgradeviawarning" + - "content-languagecontent-lengthcontent-locationcontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookie" + - "MondayTuesdayWednesdayThursdayFridaySaturdaySunday" + - "JanFebMarAprMayJunJulAugSepOctNovDec" + - "chunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplication/xhtmltext/plainpublicmax-age" + - "charset=iso-8859-1utf-8gzipdeflateHTTP/1.1statusversionurl\x00" - -// A SPDY specific error. -type ErrorCode string - -const ( - UnlowercasedHeaderName ErrorCode = "header was not lowercased" - DuplicateHeaders ErrorCode = "multiple headers with same name" - WrongCompressedPayloadSize ErrorCode = "compressed payload size was incorrect" - UnknownFrameType ErrorCode = "unknown frame type" - InvalidControlFrame ErrorCode = "invalid control frame" - InvalidDataFrame ErrorCode = "invalid data frame" - InvalidHeaderPresent ErrorCode = "frame contained invalid header" -) - -// Error contains both the type of error and additional values. StreamId is 0 -// if Error is not associated with a stream. -type Error struct { - Err ErrorCode - StreamId uint32 -} - -func (e *Error) Error() string { - return string(e.Err) -} - -var invalidReqHeaders = map[string]bool{ - "Connection": true, - "Keep-Alive": true, - "Proxy-Connection": true, - "Transfer-Encoding": true, -} - -var invalidRespHeaders = map[string]bool{ - "Connection": true, - "Keep-Alive": true, - "Transfer-Encoding": true, -} - -// Framer handles serializing/deserializing SPDY frames, including compressing/ -// decompressing payloads. -type Framer struct { - headerCompressionDisabled bool - w io.Writer - headerBuf *bytes.Buffer - headerCompressor *zlib.Writer - r io.Reader - headerReader io.LimitedReader - headerDecompressor io.ReadCloser -} - -// NewFramer allocates a new Framer for a given SPDY connection, repesented by -// a io.Writer and io.Reader. Note that Framer will read and write individual fields -// from/to the Reader and Writer, so the caller should pass in an appropriately -// buffered implementation to optimize performance. -func NewFramer(w io.Writer, r io.Reader) (*Framer, error) { - compressBuf := new(bytes.Buffer) - compressor, err := zlib.NewWriterLevelDict(compressBuf, zlib.BestCompression, []byte(HeaderDictionary)) - if err != nil { - return nil, err - } - framer := &Framer{ - w: w, - headerBuf: compressBuf, - headerCompressor: compressor, - r: r, - } - return framer, nil -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/write.go b/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/write.go deleted file mode 100644 index 3dd2ca1..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/write.go +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package spdy - -import ( - "encoding/binary" - "io" - "net/http" - "strings" -) - -func (frame *SynStreamFrame) write(f *Framer) error { - return f.writeSynStreamFrame(frame) -} - -func (frame *SynReplyFrame) write(f *Framer) error { - return f.writeSynReplyFrame(frame) -} - -func (frame *RstStreamFrame) write(f *Framer) (err error) { - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypeRstStream - frame.CFHeader.length = 8 - - // Serialize frame to Writer - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, frame.Status); err != nil { - return - } - return -} - -func (frame *SettingsFrame) write(f *Framer) (err error) { - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypeSettings - frame.CFHeader.length = uint32(len(frame.FlagIdValues)*8 + 4) - - // Serialize frame to Writer - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, uint32(len(frame.FlagIdValues))); err != nil { - return - } - for _, flagIdValue := range frame.FlagIdValues { - flagId := (uint32(flagIdValue.Flag) << 24) | uint32(flagIdValue.Id) - if err = binary.Write(f.w, binary.BigEndian, flagId); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, flagIdValue.Value); err != nil { - return - } - } - return -} - -func (frame *NoopFrame) write(f *Framer) error { - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypeNoop - - // Serialize frame to Writer - return writeControlFrameHeader(f.w, frame.CFHeader) -} - -func (frame *PingFrame) write(f *Framer) (err error) { - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypePing - frame.CFHeader.length = 4 - - // Serialize frame to Writer - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, frame.Id); err != nil { - return - } - return -} - -func (frame *GoAwayFrame) write(f *Framer) (err error) { - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypeGoAway - frame.CFHeader.length = 4 - - // Serialize frame to Writer - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, frame.LastGoodStreamId); err != nil { - return - } - return nil -} - -func (frame *HeadersFrame) write(f *Framer) error { - return f.writeHeadersFrame(frame) -} - -func (frame *DataFrame) write(f *Framer) error { - return f.writeDataFrame(frame) -} - -// WriteFrame writes a frame. -func (f *Framer) WriteFrame(frame Frame) error { - return frame.write(f) -} - -func writeControlFrameHeader(w io.Writer, h ControlFrameHeader) error { - if err := binary.Write(w, binary.BigEndian, 0x8000|h.version); err != nil { - return err - } - if err := binary.Write(w, binary.BigEndian, h.frameType); err != nil { - return err - } - flagsAndLength := (uint32(h.Flags) << 24) | h.length - if err := binary.Write(w, binary.BigEndian, flagsAndLength); err != nil { - return err - } - return nil -} - -func writeHeaderValueBlock(w io.Writer, h http.Header) (n int, err error) { - n = 0 - if err = binary.Write(w, binary.BigEndian, uint16(len(h))); err != nil { - return - } - n += 2 - for name, values := range h { - if err = binary.Write(w, binary.BigEndian, uint16(len(name))); err != nil { - return - } - n += 2 - name = strings.ToLower(name) - if _, err = io.WriteString(w, name); err != nil { - return - } - n += len(name) - v := strings.Join(values, "\x00") - if err = binary.Write(w, binary.BigEndian, uint16(len(v))); err != nil { - return - } - n += 2 - if _, err = io.WriteString(w, v); err != nil { - return - } - n += len(v) - } - return -} - -func (f *Framer) writeSynStreamFrame(frame *SynStreamFrame) (err error) { - // Marshal the headers. - var writer io.Writer = f.headerBuf - if !f.headerCompressionDisabled { - writer = f.headerCompressor - } - if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { - return - } - if !f.headerCompressionDisabled { - f.headerCompressor.Flush() - } - - // Set ControlFrameHeader - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypeSynStream - frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 10) - - // Serialize frame to Writer - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return err - } - if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { - return err - } - if err = binary.Write(f.w, binary.BigEndian, frame.AssociatedToStreamId); err != nil { - return err - } - if err = binary.Write(f.w, binary.BigEndian, frame.Priority<<14); err != nil { - return err - } - if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { - return err - } - f.headerBuf.Reset() - return nil -} - -func (f *Framer) writeSynReplyFrame(frame *SynReplyFrame) (err error) { - // Marshal the headers. - var writer io.Writer = f.headerBuf - if !f.headerCompressionDisabled { - writer = f.headerCompressor - } - if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { - return - } - if !f.headerCompressionDisabled { - f.headerCompressor.Flush() - } - - // Set ControlFrameHeader - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypeSynReply - frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 6) - - // Serialize frame to Writer - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, uint16(0)); err != nil { - return - } - if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { - return - } - f.headerBuf.Reset() - return -} - -func (f *Framer) writeHeadersFrame(frame *HeadersFrame) (err error) { - // Marshal the headers. - var writer io.Writer = f.headerBuf - if !f.headerCompressionDisabled { - writer = f.headerCompressor - } - if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { - return - } - if !f.headerCompressionDisabled { - f.headerCompressor.Flush() - } - - // Set ControlFrameHeader - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypeHeaders - frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 6) - - // Serialize frame to Writer - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, uint16(0)); err != nil { - return - } - if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { - return - } - f.headerBuf.Reset() - return -} - -func (f *Framer) writeDataFrame(frame *DataFrame) (err error) { - // Validate DataFrame - if frame.StreamId&0x80000000 != 0 || len(frame.Data) >= 0x0f000000 { - return &Error{InvalidDataFrame, frame.StreamId} - } - - // Serialize frame to Writer - if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { - return - } - flagsAndLength := (uint32(frame.Flags) << 24) | uint32(len(frame.Data)) - if err = binary.Write(f.w, binary.BigEndian, flagsAndLength); err != nil { - return - } - if _, err = f.w.Write(frame.Data); err != nil { - return - } - - return nil -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/client.go b/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/client.go deleted file mode 100644 index 1b82e9c..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/client.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bufio" - "crypto/tls" - "io" - "net" - "net/url" -) - -// DialError is an error that occurs while dialling a websocket server. -type DialError struct { - *Config - Err error -} - -func (e *DialError) Error() string { - return "websocket.Dial " + e.Config.Location.String() + ": " + e.Err.Error() -} - -// NewConfig creates a new WebSocket config for client connection. -func NewConfig(server, origin string) (config *Config, err error) { - config = new(Config) - config.Version = ProtocolVersionHybi13 - config.Location, err = url.ParseRequestURI(server) - if err != nil { - return - } - config.Origin, err = url.ParseRequestURI(origin) - if err != nil { - return - } - return -} - -// NewClient creates a new WebSocket client connection over rwc. -func NewClient(config *Config, rwc io.ReadWriteCloser) (ws *Conn, err error) { - br := bufio.NewReader(rwc) - bw := bufio.NewWriter(rwc) - switch config.Version { - case ProtocolVersionHixie75: - err = hixie75ClientHandshake(config, br, bw) - case ProtocolVersionHixie76, ProtocolVersionHybi00: - err = hixie76ClientHandshake(config, br, bw) - case ProtocolVersionHybi08, ProtocolVersionHybi13: - err = hybiClientHandshake(config, br, bw) - default: - err = ErrBadProtocolVersion - } - if err != nil { - return - } - buf := bufio.NewReadWriter(br, bw) - switch config.Version { - case ProtocolVersionHixie75, ProtocolVersionHixie76, ProtocolVersionHybi00: - ws = newHixieClientConn(config, buf, rwc) - case ProtocolVersionHybi08, ProtocolVersionHybi13: - ws = newHybiClientConn(config, buf, rwc) - } - return -} - -/* -Dial opens a new client connection to a WebSocket. - -A trivial example client: - - package main - - import ( - "log" - "net/http" - "strings" - "websocket" - ) - - func main() { - origin := "http://localhost/" - url := "ws://localhost/ws" - ws, err := websocket.Dial(url, "", origin) - if err != nil { - log.Fatal(err) - } - if _, err := ws.Write([]byte("hello, world!\n")); err != nil { - log.Fatal(err) - } - var msg = make([]byte, 512); - if n, err := ws.Read(msg); err != nil { - log.Fatal(err) - } - // use msg[0:n] - } -*/ -func Dial(url_, protocol, origin string) (ws *Conn, err error) { - config, err := NewConfig(url_, origin) - if err != nil { - return nil, err - } - return DialConfig(config) -} - -// DialConfig opens a new client connection to a WebSocket with a config. -func DialConfig(config *Config) (ws *Conn, err error) { - var client net.Conn - if config.Location == nil { - return nil, &DialError{config, ErrBadWebSocketLocation} - } - if config.Origin == nil { - return nil, &DialError{config, ErrBadWebSocketOrigin} - } - switch config.Location.Scheme { - case "ws": - client, err = net.Dial("tcp", config.Location.Host) - - case "wss": - client, err = tls.Dial("tcp", config.Location.Host, config.TlsConfig) - - default: - err = ErrBadScheme - } - if err != nil { - goto Error - } - - ws, err = NewClient(config, client) - if err != nil { - goto Error - } - return - -Error: - return nil, &DialError{config, err} -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hixie.go b/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hixie.go deleted file mode 100644 index 6d215b9..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hixie.go +++ /dev/null @@ -1,695 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -// This file implements a protocol of Hixie draft version 75 and 76 -// (draft 76 equals to hybi 00) - -import ( - "bufio" - "bytes" - "crypto/md5" - "encoding/binary" - "fmt" - "io" - "io/ioutil" - "math/rand" - "net/http" - "net/url" - "strconv" - "strings" -) - -// An aray of characters to be randomly inserted to construct Sec-WebSocket-Key -// value. It holds characters from ranges U+0021 to U+002F and U+003A to U+007E. -// See Step 21 in Section 4.1 Opening handshake. -// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00#page-22 -var secKeyRandomChars [0x30 - 0x21 + 0x7F - 0x3A]byte - -func init() { - i := 0 - for ch := byte(0x21); ch < 0x30; ch++ { - secKeyRandomChars[i] = ch - i++ - } - for ch := byte(0x3a); ch < 0x7F; ch++ { - secKeyRandomChars[i] = ch - i++ - } -} - -type byteReader interface { - ReadByte() (byte, error) -} - -// readHixieLength reads frame length for frame type 0x80-0xFF -// as defined in Hixie draft. -// See section 4.2 Data framing. -// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00#section-4.2 -func readHixieLength(r byteReader) (length int64, lengthFields []byte, err error) { - for { - c, err := r.ReadByte() - if err != nil { - return 0, nil, err - } - lengthFields = append(lengthFields, c) - length = length*128 + int64(c&0x7f) - if c&0x80 == 0 { - break - } - } - return -} - -// A hixieLengthFrameReader is a reader for frame type 0x80-0xFF -// as defined in hixie draft. -type hixieLengthFrameReader struct { - reader io.Reader - FrameType byte - Length int64 - header *bytes.Buffer - length int -} - -func (frame *hixieLengthFrameReader) Read(msg []byte) (n int, err error) { - return frame.reader.Read(msg) -} - -func (frame *hixieLengthFrameReader) PayloadType() byte { - if frame.FrameType == '\xff' && frame.Length == 0 { - return CloseFrame - } - return UnknownFrame -} - -func (frame *hixieLengthFrameReader) HeaderReader() io.Reader { - if frame.header == nil { - return nil - } - if frame.header.Len() == 0 { - frame.header = nil - return nil - } - return frame.header -} - -func (frame *hixieLengthFrameReader) TrailerReader() io.Reader { return nil } - -func (frame *hixieLengthFrameReader) Len() (n int) { return frame.length } - -// A HixieSentinelFrameReader is a reader for frame type 0x00-0x7F -// as defined in hixie draft. -type hixieSentinelFrameReader struct { - reader *bufio.Reader - FrameType byte - header *bytes.Buffer - data []byte - seenTrailer bool - trailer *bytes.Buffer -} - -func (frame *hixieSentinelFrameReader) Read(msg []byte) (n int, err error) { - if len(frame.data) == 0 { - if frame.seenTrailer { - return 0, io.EOF - } - frame.data, err = frame.reader.ReadSlice('\xff') - if err == nil { - frame.seenTrailer = true - frame.data = frame.data[:len(frame.data)-1] // trim \xff - frame.trailer = bytes.NewBuffer([]byte{0xff}) - } - } - n = copy(msg, frame.data) - frame.data = frame.data[n:] - return n, err -} - -func (frame *hixieSentinelFrameReader) PayloadType() byte { - if frame.FrameType == 0 { - return TextFrame - } - return UnknownFrame -} - -func (frame *hixieSentinelFrameReader) HeaderReader() io.Reader { - if frame.header == nil { - return nil - } - if frame.header.Len() == 0 { - frame.header = nil - return nil - } - return frame.header -} - -func (frame *hixieSentinelFrameReader) TrailerReader() io.Reader { - if frame.trailer == nil { - return nil - } - if frame.trailer.Len() == 0 { - frame.trailer = nil - return nil - } - return frame.trailer -} - -func (frame *hixieSentinelFrameReader) Len() int { return -1 } - -// A HixieFrameReaderFactory creates new frame reader based on its frame type. -type hixieFrameReaderFactory struct { - *bufio.Reader -} - -func (buf hixieFrameReaderFactory) NewFrameReader() (r frameReader, err error) { - var header []byte - var b byte - b, err = buf.ReadByte() - if err != nil { - return - } - header = append(header, b) - if b&0x80 == 0x80 { - length, lengthFields, err := readHixieLength(buf.Reader) - if err != nil { - return nil, err - } - if length == 0 { - return nil, io.EOF - } - header = append(header, lengthFields...) - return &hixieLengthFrameReader{ - reader: io.LimitReader(buf.Reader, length), - FrameType: b, - Length: length, - header: bytes.NewBuffer(header)}, err - } - return &hixieSentinelFrameReader{ - reader: buf.Reader, - FrameType: b, - header: bytes.NewBuffer(header)}, err -} - -type hixiFrameWriter struct { - writer *bufio.Writer -} - -func (frame *hixiFrameWriter) Write(msg []byte) (n int, err error) { - frame.writer.WriteByte(0) - frame.writer.Write(msg) - frame.writer.WriteByte(0xff) - err = frame.writer.Flush() - return len(msg), err -} - -func (frame *hixiFrameWriter) Close() error { return nil } - -type hixiFrameWriterFactory struct { - *bufio.Writer -} - -func (buf hixiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err error) { - if payloadType != TextFrame { - return nil, ErrNotSupported - } - return &hixiFrameWriter{writer: buf.Writer}, nil -} - -type hixiFrameHandler struct { - conn *Conn -} - -func (handler *hixiFrameHandler) HandleFrame(frame frameReader) (r frameReader, err error) { - if header := frame.HeaderReader(); header != nil { - io.Copy(ioutil.Discard, header) - } - if frame.PayloadType() != TextFrame { - io.Copy(ioutil.Discard, frame) - return nil, nil - } - return frame, nil -} - -func (handler *hixiFrameHandler) WriteClose(_ int) (err error) { - handler.conn.wio.Lock() - defer handler.conn.wio.Unlock() - closingFrame := []byte{'\xff', '\x00'} - handler.conn.buf.Write(closingFrame) - return handler.conn.buf.Flush() -} - -// newHixiConn creates a new WebSocket connection speaking hixie draft protocol. -func newHixieConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { - if buf == nil { - br := bufio.NewReader(rwc) - bw := bufio.NewWriter(rwc) - buf = bufio.NewReadWriter(br, bw) - } - ws := &Conn{config: config, request: request, buf: buf, rwc: rwc, - frameReaderFactory: hixieFrameReaderFactory{buf.Reader}, - frameWriterFactory: hixiFrameWriterFactory{buf.Writer}, - PayloadType: TextFrame} - ws.frameHandler = &hixiFrameHandler{ws} - return ws -} - -// getChallengeResponse computes the expected response from the -// challenge as described in section 5.1 Opening Handshake steps 42 to -// 43 of http://www.whatwg.org/specs/web-socket-protocol/ -func getChallengeResponse(number1, number2 uint32, key3 []byte) (expected []byte, err error) { - // 41. Let /challenge/ be the concatenation of /number_1/, expressed - // a big-endian 32 bit integer, /number_2/, expressed in a big- - // endian 32 bit integer, and the eight bytes of /key_3/ in the - // order they were sent to the wire. - challenge := make([]byte, 16) - binary.BigEndian.PutUint32(challenge[0:], number1) - binary.BigEndian.PutUint32(challenge[4:], number2) - copy(challenge[8:], key3) - - // 42. Let /expected/ be the MD5 fingerprint of /challenge/ as a big- - // endian 128 bit string. - h := md5.New() - if _, err = h.Write(challenge); err != nil { - return - } - expected = h.Sum(nil) - return -} - -// Generates handshake key as described in 4.1 Opening handshake step 16 to 22. -// cf. http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00 -func generateKeyNumber() (key string, number uint32) { - // 16. Let /spaces_n/ be a random integer from 1 to 12 inclusive. - spaces := rand.Intn(12) + 1 - - // 17. Let /max_n/ be the largest integer not greater than - // 4,294,967,295 divided by /spaces_n/ - max := int(4294967295 / uint32(spaces)) - - // 18. Let /number_n/ be a random integer from 0 to /max_n/ inclusive. - number = uint32(rand.Intn(max + 1)) - - // 19. Let /product_n/ be the result of multiplying /number_n/ and - // /spaces_n/ together. - product := number * uint32(spaces) - - // 20. Let /key_n/ be a string consisting of /product_n/, expressed - // in base ten using the numerals in the range U+0030 DIGIT ZERO (0) - // to U+0039 DIGIT NINE (9). - key = fmt.Sprintf("%d", product) - - // 21. Insert between one and twelve random characters from the ranges - // U+0021 to U+002F and U+003A to U+007E into /key_n/ at random - // positions. - n := rand.Intn(12) + 1 - for i := 0; i < n; i++ { - pos := rand.Intn(len(key)) + 1 - ch := secKeyRandomChars[rand.Intn(len(secKeyRandomChars))] - key = key[0:pos] + string(ch) + key[pos:] - } - - // 22. Insert /spaces_n/ U+0020 SPACE characters into /key_n/ at random - // positions other than the start or end of the string. - for i := 0; i < spaces; i++ { - pos := rand.Intn(len(key)-1) + 1 - key = key[0:pos] + " " + key[pos:] - } - - return -} - -// Generates handshake key_3 as described in 4.1 Opening handshake step 26. -// cf. http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00 -func generateKey3() (key []byte) { - // 26. Let /key3/ be a string consisting of eight random bytes (or - // equivalently, a random 64 bit integer encoded in big-endian order). - key = make([]byte, 8) - for i := 0; i < 8; i++ { - key[i] = byte(rand.Intn(256)) - } - return -} - -// Cilent handhake described in (soon obsolete) -// draft-ietf-hybi-thewebsocket-protocol-00 -// (draft-hixie-thewebsocket-protocol-76) -func hixie76ClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) { - switch config.Version { - case ProtocolVersionHixie76, ProtocolVersionHybi00: - default: - panic("wrong protocol version.") - } - // 4.1. Opening handshake. - // Step 5. send a request line. - bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n") - - // Step 6-14. push request headers in fields. - fields := []string{ - "Upgrade: WebSocket\r\n", - "Connection: Upgrade\r\n", - "Host: " + config.Location.Host + "\r\n", - "Origin: " + config.Origin.String() + "\r\n", - } - if len(config.Protocol) > 0 { - if len(config.Protocol) != 1 { - return ErrBadWebSocketProtocol - } - fields = append(fields, "Sec-WebSocket-Protocol: "+config.Protocol[0]+"\r\n") - } - // TODO(ukai): Step 15. send cookie if any. - - // Step 16-23. generate keys and push Sec-WebSocket-Key in fields. - key1, number1 := generateKeyNumber() - key2, number2 := generateKeyNumber() - if config.handshakeData != nil { - key1 = config.handshakeData["key1"] - n, err := strconv.ParseUint(config.handshakeData["number1"], 10, 32) - if err != nil { - panic(err) - } - number1 = uint32(n) - key2 = config.handshakeData["key2"] - n, err = strconv.ParseUint(config.handshakeData["number2"], 10, 32) - if err != nil { - panic(err) - } - number2 = uint32(n) - } - fields = append(fields, "Sec-WebSocket-Key1: "+key1+"\r\n") - fields = append(fields, "Sec-WebSocket-Key2: "+key2+"\r\n") - - // Step 24. shuffle fields and send them out. - for i := 1; i < len(fields); i++ { - j := rand.Intn(i) - fields[i], fields[j] = fields[j], fields[i] - } - for i := 0; i < len(fields); i++ { - bw.WriteString(fields[i]) - } - // Step 25. send CRLF. - bw.WriteString("\r\n") - - // Step 26. generate 8 bytes random key. - key3 := generateKey3() - if config.handshakeData != nil { - key3 = []byte(config.handshakeData["key3"]) - } - // Step 27. send it out. - bw.Write(key3) - if err = bw.Flush(); err != nil { - return - } - - // Step 28-29, 32-40. read response from server. - resp, err := http.ReadResponse(br, &http.Request{Method: "GET"}) - if err != nil { - return err - } - // Step 30. check response code is 101. - if resp.StatusCode != 101 { - return ErrBadStatus - } - - // Step 41. check websocket headers. - if resp.Header.Get("Upgrade") != "WebSocket" || - strings.ToLower(resp.Header.Get("Connection")) != "upgrade" { - return ErrBadUpgrade - } - - if resp.Header.Get("Sec-Websocket-Origin") != config.Origin.String() { - return ErrBadWebSocketOrigin - } - - if resp.Header.Get("Sec-Websocket-Location") != config.Location.String() { - return ErrBadWebSocketLocation - } - - if len(config.Protocol) > 0 && resp.Header.Get("Sec-Websocket-Protocol") != config.Protocol[0] { - return ErrBadWebSocketProtocol - } - - // Step 42-43. get expected data from challenge data. - expected, err := getChallengeResponse(number1, number2, key3) - if err != nil { - return err - } - - // Step 44. read 16 bytes from server. - reply := make([]byte, 16) - if _, err = io.ReadFull(br, reply); err != nil { - return err - } - - // Step 45. check the reply equals to expected data. - if !bytes.Equal(expected, reply) { - return ErrChallengeResponse - } - // WebSocket connection is established. - return -} - -// Client Handshake described in (soon obsolete) -// draft-hixie-thewebsocket-protocol-75. -func hixie75ClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) { - if config.Version != ProtocolVersionHixie75 { - panic("wrong protocol version.") - } - bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n") - bw.WriteString("Upgrade: WebSocket\r\n") - bw.WriteString("Connection: Upgrade\r\n") - bw.WriteString("Host: " + config.Location.Host + "\r\n") - bw.WriteString("Origin: " + config.Origin.String() + "\r\n") - if len(config.Protocol) > 0 { - if len(config.Protocol) != 1 { - return ErrBadWebSocketProtocol - } - bw.WriteString("WebSocket-Protocol: " + config.Protocol[0] + "\r\n") - } - bw.WriteString("\r\n") - bw.Flush() - resp, err := http.ReadResponse(br, &http.Request{Method: "GET"}) - if err != nil { - return - } - if resp.Status != "101 Web Socket Protocol Handshake" { - return ErrBadStatus - } - if resp.Header.Get("Upgrade") != "WebSocket" || - resp.Header.Get("Connection") != "Upgrade" { - return ErrBadUpgrade - } - if resp.Header.Get("Websocket-Origin") != config.Origin.String() { - return ErrBadWebSocketOrigin - } - if resp.Header.Get("Websocket-Location") != config.Location.String() { - return ErrBadWebSocketLocation - } - if len(config.Protocol) > 0 && resp.Header.Get("Websocket-Protocol") != config.Protocol[0] { - return ErrBadWebSocketProtocol - } - return -} - -// newHixieClientConn returns new WebSocket connection speaking hixie draft protocol. -func newHixieClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn { - return newHixieConn(config, buf, rwc, nil) -} - -// Gets key number from Sec-WebSocket-Key: field as described -// in 5.2 Sending the server's opening handshake, 4. -func getKeyNumber(s string) (r uint32) { - // 4. Let /key-number_n/ be the digits (characters in the range - // U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9)) in /key_1/, - // interpreted as a base ten integer, ignoring all other characters - // in /key_n/. - r = 0 - for i := 0; i < len(s); i++ { - if s[i] >= '0' && s[i] <= '9' { - r = r*10 + uint32(s[i]) - '0' - } - } - return -} - -// A Hixie76ServerHandshaker performs a server handshake using -// hixie draft 76 protocol. -type hixie76ServerHandshaker struct { - *Config - challengeResponse []byte -} - -func (c *hixie76ServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) { - c.Version = ProtocolVersionHybi00 - if req.Method != "GET" { - return http.StatusMethodNotAllowed, ErrBadRequestMethod - } - // HTTP version can be safely ignored. - - if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" || - strings.ToLower(req.Header.Get("Connection")) != "upgrade" { - return http.StatusBadRequest, ErrNotWebSocket - } - - // TODO(ukai): check Host - c.Origin, err = url.ParseRequestURI(req.Header.Get("Origin")) - if err != nil { - return http.StatusBadRequest, err - } - - key1 := req.Header.Get("Sec-Websocket-Key1") - if key1 == "" { - return http.StatusBadRequest, ErrChallengeResponse - } - key2 := req.Header.Get("Sec-Websocket-Key2") - if key2 == "" { - return http.StatusBadRequest, ErrChallengeResponse - } - key3 := make([]byte, 8) - if _, err := io.ReadFull(buf, key3); err != nil { - return http.StatusBadRequest, ErrChallengeResponse - } - - var scheme string - if req.TLS != nil { - scheme = "wss" - } else { - scheme = "ws" - } - c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI()) - if err != nil { - return http.StatusBadRequest, err - } - - // Step 4. get key number in Sec-WebSocket-Key fields. - keyNumber1 := getKeyNumber(key1) - keyNumber2 := getKeyNumber(key2) - - // Step 5. get number of spaces in Sec-WebSocket-Key fields. - space1 := uint32(strings.Count(key1, " ")) - space2 := uint32(strings.Count(key2, " ")) - if space1 == 0 || space2 == 0 { - return http.StatusBadRequest, ErrChallengeResponse - } - - // Step 6. key number must be an integral multiple of spaces. - if keyNumber1%space1 != 0 || keyNumber2%space2 != 0 { - return http.StatusBadRequest, ErrChallengeResponse - } - - // Step 7. let part be key number divided by spaces. - part1 := keyNumber1 / space1 - part2 := keyNumber2 / space2 - - // Step 8. let challenge be concatenation of part1, part2 and key3. - // Step 9. get MD5 fingerprint of challenge. - c.challengeResponse, err = getChallengeResponse(part1, part2, key3) - if err != nil { - return http.StatusInternalServerError, err - } - protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol")) - protocols := strings.Split(protocol, ",") - for i := 0; i < len(protocols); i++ { - c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i])) - } - - return http.StatusSwitchingProtocols, nil -} - -func (c *hixie76ServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) { - if len(c.Protocol) > 0 { - if len(c.Protocol) != 1 { - return ErrBadWebSocketProtocol - } - } - - // Step 10. send response status line. - buf.WriteString("HTTP/1.1 101 WebSocket Protocol Handshake\r\n") - // Step 11. send response headers. - buf.WriteString("Upgrade: WebSocket\r\n") - buf.WriteString("Connection: Upgrade\r\n") - buf.WriteString("Sec-WebSocket-Origin: " + c.Origin.String() + "\r\n") - buf.WriteString("Sec-WebSocket-Location: " + c.Location.String() + "\r\n") - if len(c.Protocol) > 0 { - buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n") - } - // Step 12. send CRLF. - buf.WriteString("\r\n") - // Step 13. send response data. - buf.Write(c.challengeResponse) - return buf.Flush() -} - -func (c *hixie76ServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) { - return newHixieServerConn(c.Config, buf, rwc, request) -} - -// A hixie75ServerHandshaker performs a server handshake using -// hixie draft 75 protocol. -type hixie75ServerHandshaker struct { - *Config -} - -func (c *hixie75ServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) { - c.Version = ProtocolVersionHixie75 - if req.Method != "GET" || req.Proto != "HTTP/1.1" { - return http.StatusMethodNotAllowed, ErrBadRequestMethod - } - if req.Header.Get("Upgrade") != "WebSocket" { - return http.StatusBadRequest, ErrNotWebSocket - } - if req.Header.Get("Connection") != "Upgrade" { - return http.StatusBadRequest, ErrNotWebSocket - } - c.Origin, err = url.ParseRequestURI(strings.TrimSpace(req.Header.Get("Origin"))) - if err != nil { - return http.StatusBadRequest, err - } - - var scheme string - if req.TLS != nil { - scheme = "wss" - } else { - scheme = "ws" - } - c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI()) - if err != nil { - return http.StatusBadRequest, err - } - protocol := strings.TrimSpace(req.Header.Get("Websocket-Protocol")) - protocols := strings.Split(protocol, ",") - for i := 0; i < len(protocols); i++ { - c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i])) - } - - return http.StatusSwitchingProtocols, nil -} - -func (c *hixie75ServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) { - if len(c.Protocol) > 0 { - if len(c.Protocol) != 1 { - return ErrBadWebSocketProtocol - } - } - - buf.WriteString("HTTP/1.1 101 Web Socket Protocol Handshake\r\n") - buf.WriteString("Upgrade: WebSocket\r\n") - buf.WriteString("Connection: Upgrade\r\n") - buf.WriteString("WebSocket-Origin: " + c.Origin.String() + "\r\n") - buf.WriteString("WebSocket-Location: " + c.Location.String() + "\r\n") - if len(c.Protocol) > 0 { - buf.WriteString("WebSocket-Protocol: " + c.Protocol[0] + "\r\n") - } - buf.WriteString("\r\n") - return buf.Flush() -} - -func (c *hixie75ServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) { - return newHixieServerConn(c.Config, buf, rwc, request) -} - -// newHixieServerConn returns a new WebSocket connection speaking hixie draft protocol. -func newHixieServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { - return newHixieConn(config, buf, rwc, request) -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hixie_test.go b/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hixie_test.go deleted file mode 100644 index 8f387dd..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hixie_test.go +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bufio" - "bytes" - "fmt" - "io" - "net/http" - "net/url" - "strings" - "testing" -) - -// Test the getChallengeResponse function with values from section -// 5.1 of the specification steps 18, 26, and 43 from -// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00 -func TestHixie76Challenge(t *testing.T) { - var part1 uint32 = 777007543 - var part2 uint32 = 114997259 - key3 := []byte{0x47, 0x30, 0x22, 0x2D, 0x5A, 0x3F, 0x47, 0x58} - expected := []byte("0st3Rl&q-2ZU^weu") - - response, err := getChallengeResponse(part1, part2, key3) - if err != nil { - t.Errorf("getChallengeResponse: returned error %v", err) - return - } - if !bytes.Equal(expected, response) { - t.Errorf("getChallengeResponse: expected %q got %q", expected, response) - } -} - -func TestHixie76ClientHandshake(t *testing.T) { - b := bytes.NewBuffer([]byte{}) - bw := bufio.NewWriter(b) - br := bufio.NewReader(strings.NewReader(`HTTP/1.1 101 WebSocket Protocol Handshake -Upgrade: WebSocket -Connection: Upgrade -Sec-WebSocket-Origin: http://example.com -Sec-WebSocket-Location: ws://example.com/demo -Sec-WebSocket-Protocol: sample - -8jKS'y:G*Co,Wxa-`)) - - var err error - config := new(Config) - config.Location, err = url.ParseRequestURI("ws://example.com/demo") - if err != nil { - t.Fatal("location url", err) - } - config.Origin, err = url.ParseRequestURI("http://example.com") - if err != nil { - t.Fatal("origin url", err) - } - config.Protocol = append(config.Protocol, "sample") - config.Version = ProtocolVersionHixie76 - - config.handshakeData = map[string]string{ - "key1": "4 @1 46546xW%0l 1 5", - "number1": "829309203", - "key2": "12998 5 Y3 1 .P00", - "number2": "259970620", - "key3": "^n:ds[4U", - } - err = hixie76ClientHandshake(config, br, bw) - if err != nil { - t.Errorf("handshake failed: %v", err) - } - req, err := http.ReadRequest(bufio.NewReader(b)) - if err != nil { - t.Fatalf("read request: %v", err) - } - if req.Method != "GET" { - t.Errorf("request method expected GET, but got %q", req.Method) - } - if req.URL.Path != "/demo" { - t.Errorf("request path expected /demo, but got %q", req.URL.Path) - } - if req.Proto != "HTTP/1.1" { - t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto) - } - if req.Host != "example.com" { - t.Errorf("request Host expected example.com, but got %v", req.Host) - } - var expectedHeader = map[string]string{ - "Connection": "Upgrade", - "Upgrade": "WebSocket", - "Origin": "http://example.com", - "Sec-Websocket-Key1": config.handshakeData["key1"], - "Sec-Websocket-Key2": config.handshakeData["key2"], - "Sec-WebSocket-Protocol": config.Protocol[0], - } - for k, v := range expectedHeader { - if req.Header.Get(k) != v { - t.Errorf(fmt.Sprintf("%s expected %q but got %q", k, v, req.Header.Get(k))) - } - } -} - -func TestHixie76ServerHandshake(t *testing.T) { - config := new(Config) - handshaker := &hixie76ServerHandshaker{Config: config} - br := bufio.NewReader(strings.NewReader(`GET /demo HTTP/1.1 -Host: example.com -Connection: Upgrade -Sec-WebSocket-Key2: 12998 5 Y3 1 .P00 -Sec-WebSocket-Protocol: sample -Upgrade: WebSocket -Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5 -Origin: http://example.com - -^n:ds[4U`)) - req, err := http.ReadRequest(br) - if err != nil { - t.Fatal("request", err) - } - code, err := handshaker.ReadHandshake(br, req) - if err != nil { - t.Errorf("handshake failed: %v", err) - } - if code != http.StatusSwitchingProtocols { - t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code) - } - b := bytes.NewBuffer([]byte{}) - bw := bufio.NewWriter(b) - - err = handshaker.AcceptHandshake(bw) - if err != nil { - t.Errorf("handshake response failed: %v", err) - } - expectedResponse := strings.Join([]string{ - "HTTP/1.1 101 WebSocket Protocol Handshake", - "Upgrade: WebSocket", - "Connection: Upgrade", - "Sec-WebSocket-Origin: http://example.com", - "Sec-WebSocket-Location: ws://example.com/demo", - "Sec-WebSocket-Protocol: sample", - "", ""}, "\r\n") + "8jKS'y:G*Co,Wxa-" - if b.String() != expectedResponse { - t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) - } -} - -func TestHixie76SkipLengthFrame(t *testing.T) { - b := []byte{'\x80', '\x01', 'x', 0, 'h', 'e', 'l', 'l', 'o', '\xff'} - buf := bytes.NewBuffer(b) - br := bufio.NewReader(buf) - bw := bufio.NewWriter(buf) - config := newConfig(t, "/") - ws := newHixieConn(config, bufio.NewReadWriter(br, bw), nil, nil) - msg := make([]byte, 5) - n, err := ws.Read(msg) - if err != nil { - t.Errorf("Read: %v", err) - } - if !bytes.Equal(b[4:9], msg[0:n]) { - t.Errorf("Read: expected %q got %q", b[4:9], msg[0:n]) - } -} - -func TestHixie76SkipNoUTF8Frame(t *testing.T) { - b := []byte{'\x01', 'n', '\xff', 0, 'h', 'e', 'l', 'l', 'o', '\xff'} - buf := bytes.NewBuffer(b) - br := bufio.NewReader(buf) - bw := bufio.NewWriter(buf) - config := newConfig(t, "/") - ws := newHixieConn(config, bufio.NewReadWriter(br, bw), nil, nil) - msg := make([]byte, 5) - n, err := ws.Read(msg) - if err != nil { - t.Errorf("Read: %v", err) - } - if !bytes.Equal(b[4:9], msg[0:n]) { - t.Errorf("Read: expected %q got %q", b[4:9], msg[0:n]) - } -} - -func TestHixie76ClosingFrame(t *testing.T) { - b := []byte{0, 'h', 'e', 'l', 'l', 'o', '\xff'} - buf := bytes.NewBuffer(b) - br := bufio.NewReader(buf) - bw := bufio.NewWriter(buf) - config := newConfig(t, "/") - ws := newHixieConn(config, bufio.NewReadWriter(br, bw), nil, nil) - msg := make([]byte, 5) - n, err := ws.Read(msg) - if err != nil { - t.Errorf("read: %v", err) - } - if !bytes.Equal(b[1:6], msg[0:n]) { - t.Errorf("Read: expected %q got %q", b[1:6], msg[0:n]) - } - n, err = ws.Read(msg) - if err != io.EOF { - t.Errorf("read: %v", err) - } -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hybi.go b/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hybi.go deleted file mode 100644 index ab18ffc..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hybi.go +++ /dev/null @@ -1,549 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -// This file implements a protocol of hybi draft. -// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 - -import ( - "bufio" - "bytes" - "crypto/rand" - "crypto/sha1" - "encoding/base64" - "encoding/binary" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/url" - "strings" -) - -const ( - websocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" - - closeStatusNormal = 1000 - closeStatusGoingAway = 1001 - closeStatusProtocolError = 1002 - closeStatusUnsupportedData = 1003 - closeStatusFrameTooLarge = 1004 - closeStatusNoStatusRcvd = 1005 - closeStatusAbnormalClosure = 1006 - closeStatusBadMessageData = 1007 - closeStatusPolicyViolation = 1008 - closeStatusTooBigData = 1009 - closeStatusExtensionMismatch = 1010 - - maxControlFramePayloadLength = 125 -) - -var ( - ErrBadMaskingKey = &ProtocolError{"bad masking key"} - ErrBadPongMessage = &ProtocolError{"bad pong message"} - ErrBadClosingStatus = &ProtocolError{"bad closing status"} - ErrUnsupportedExtensions = &ProtocolError{"unsupported extensions"} - ErrNotImplemented = &ProtocolError{"not implemented"} -) - -// A hybiFrameHeader is a frame header as defined in hybi draft. -type hybiFrameHeader struct { - Fin bool - Rsv [3]bool - OpCode byte - Length int64 - MaskingKey []byte - - data *bytes.Buffer -} - -// A hybiFrameReader is a reader for hybi frame. -type hybiFrameReader struct { - reader io.Reader - - header hybiFrameHeader - pos int64 - length int -} - -func (frame *hybiFrameReader) Read(msg []byte) (n int, err error) { - n, err = frame.reader.Read(msg) - if err != nil { - return 0, err - } - if frame.header.MaskingKey != nil { - for i := 0; i < n; i++ { - msg[i] = msg[i] ^ frame.header.MaskingKey[frame.pos%4] - frame.pos++ - } - } - return n, err -} - -func (frame *hybiFrameReader) PayloadType() byte { return frame.header.OpCode } - -func (frame *hybiFrameReader) HeaderReader() io.Reader { - if frame.header.data == nil { - return nil - } - if frame.header.data.Len() == 0 { - return nil - } - return frame.header.data -} - -func (frame *hybiFrameReader) TrailerReader() io.Reader { return nil } - -func (frame *hybiFrameReader) Len() (n int) { return frame.length } - -// A hybiFrameReaderFactory creates new frame reader based on its frame type. -type hybiFrameReaderFactory struct { - *bufio.Reader -} - -// NewFrameReader reads a frame header from the connection, and creates new reader for the frame. -// See Section 5.2 Base Frameing protocol for detail. -// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5.2 -func (buf hybiFrameReaderFactory) NewFrameReader() (frame frameReader, err error) { - hybiFrame := new(hybiFrameReader) - frame = hybiFrame - var header []byte - var b byte - // First byte. FIN/RSV1/RSV2/RSV3/OpCode(4bits) - b, err = buf.ReadByte() - if err != nil { - return - } - header = append(header, b) - hybiFrame.header.Fin = ((header[0] >> 7) & 1) != 0 - for i := 0; i < 3; i++ { - j := uint(6 - i) - hybiFrame.header.Rsv[i] = ((header[0] >> j) & 1) != 0 - } - hybiFrame.header.OpCode = header[0] & 0x0f - - // Second byte. Mask/Payload len(7bits) - b, err = buf.ReadByte() - if err != nil { - return - } - header = append(header, b) - mask := (b & 0x80) != 0 - b &= 0x7f - lengthFields := 0 - switch { - case b <= 125: // Payload length 7bits. - hybiFrame.header.Length = int64(b) - case b == 126: // Payload length 7+16bits - lengthFields = 2 - case b == 127: // Payload length 7+64bits - lengthFields = 8 - } - for i := 0; i < lengthFields; i++ { - b, err = buf.ReadByte() - if err != nil { - return - } - header = append(header, b) - hybiFrame.header.Length = hybiFrame.header.Length*256 + int64(b) - } - if mask { - // Masking key. 4 bytes. - for i := 0; i < 4; i++ { - b, err = buf.ReadByte() - if err != nil { - return - } - header = append(header, b) - hybiFrame.header.MaskingKey = append(hybiFrame.header.MaskingKey, b) - } - } - hybiFrame.reader = io.LimitReader(buf.Reader, hybiFrame.header.Length) - hybiFrame.header.data = bytes.NewBuffer(header) - hybiFrame.length = len(header) + int(hybiFrame.header.Length) - return -} - -// A HybiFrameWriter is a writer for hybi frame. -type hybiFrameWriter struct { - writer *bufio.Writer - - header *hybiFrameHeader -} - -func (frame *hybiFrameWriter) Write(msg []byte) (n int, err error) { - var header []byte - var b byte - if frame.header.Fin { - b |= 0x80 - } - for i := 0; i < 3; i++ { - if frame.header.Rsv[i] { - j := uint(6 - i) - b |= 1 << j - } - } - b |= frame.header.OpCode - header = append(header, b) - if frame.header.MaskingKey != nil { - b = 0x80 - } else { - b = 0 - } - lengthFields := 0 - length := len(msg) - switch { - case length <= 125: - b |= byte(length) - case length < 65536: - b |= 126 - lengthFields = 2 - default: - b |= 127 - lengthFields = 8 - } - header = append(header, b) - for i := 0; i < lengthFields; i++ { - j := uint((lengthFields - i - 1) * 8) - b = byte((length >> j) & 0xff) - header = append(header, b) - } - if frame.header.MaskingKey != nil { - if len(frame.header.MaskingKey) != 4 { - return 0, ErrBadMaskingKey - } - header = append(header, frame.header.MaskingKey...) - frame.writer.Write(header) - var data []byte - - for i := 0; i < length; i++ { - data = append(data, msg[i]^frame.header.MaskingKey[i%4]) - } - frame.writer.Write(data) - err = frame.writer.Flush() - return length, err - } - frame.writer.Write(header) - frame.writer.Write(msg) - err = frame.writer.Flush() - return length, err -} - -func (frame *hybiFrameWriter) Close() error { return nil } - -type hybiFrameWriterFactory struct { - *bufio.Writer - needMaskingKey bool -} - -func (buf hybiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err error) { - frameHeader := &hybiFrameHeader{Fin: true, OpCode: payloadType} - if buf.needMaskingKey { - frameHeader.MaskingKey, err = generateMaskingKey() - if err != nil { - return nil, err - } - } - return &hybiFrameWriter{writer: buf.Writer, header: frameHeader}, nil -} - -type hybiFrameHandler struct { - conn *Conn - payloadType byte -} - -func (handler *hybiFrameHandler) HandleFrame(frame frameReader) (r frameReader, err error) { - if handler.conn.IsServerConn() { - // The client MUST mask all frames sent to the server. - if frame.(*hybiFrameReader).header.MaskingKey == nil { - handler.WriteClose(closeStatusProtocolError) - return nil, io.EOF - } - } else { - // The server MUST NOT mask all frames. - if frame.(*hybiFrameReader).header.MaskingKey != nil { - handler.WriteClose(closeStatusProtocolError) - return nil, io.EOF - } - } - if header := frame.HeaderReader(); header != nil { - io.Copy(ioutil.Discard, header) - } - switch frame.PayloadType() { - case ContinuationFrame: - frame.(*hybiFrameReader).header.OpCode = handler.payloadType - case TextFrame, BinaryFrame: - handler.payloadType = frame.PayloadType() - case CloseFrame: - return nil, io.EOF - case PingFrame: - pingMsg := make([]byte, maxControlFramePayloadLength) - n, err := io.ReadFull(frame, pingMsg) - if err != nil && err != io.ErrUnexpectedEOF { - return nil, err - } - io.Copy(ioutil.Discard, frame) - n, err = handler.WritePong(pingMsg[:n]) - if err != nil { - return nil, err - } - return nil, nil - case PongFrame: - return nil, ErrNotImplemented - } - return frame, nil -} - -func (handler *hybiFrameHandler) WriteClose(status int) (err error) { - handler.conn.wio.Lock() - defer handler.conn.wio.Unlock() - w, err := handler.conn.frameWriterFactory.NewFrameWriter(CloseFrame) - if err != nil { - return err - } - msg := make([]byte, 2) - binary.BigEndian.PutUint16(msg, uint16(status)) - _, err = w.Write(msg) - w.Close() - return err -} - -func (handler *hybiFrameHandler) WritePong(msg []byte) (n int, err error) { - handler.conn.wio.Lock() - defer handler.conn.wio.Unlock() - w, err := handler.conn.frameWriterFactory.NewFrameWriter(PongFrame) - if err != nil { - return 0, err - } - n, err = w.Write(msg) - w.Close() - return n, err -} - -// newHybiConn creates a new WebSocket connection speaking hybi draft protocol. -func newHybiConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { - if buf == nil { - br := bufio.NewReader(rwc) - bw := bufio.NewWriter(rwc) - buf = bufio.NewReadWriter(br, bw) - } - ws := &Conn{config: config, request: request, buf: buf, rwc: rwc, - frameReaderFactory: hybiFrameReaderFactory{buf.Reader}, - frameWriterFactory: hybiFrameWriterFactory{ - buf.Writer, request == nil}, - PayloadType: TextFrame, - defaultCloseStatus: closeStatusNormal} - ws.frameHandler = &hybiFrameHandler{conn: ws} - return ws -} - -// generateMaskingKey generates a masking key for a frame. -func generateMaskingKey() (maskingKey []byte, err error) { - maskingKey = make([]byte, 4) - if _, err = io.ReadFull(rand.Reader, maskingKey); err != nil { - return - } - return -} - -// genetateNonce geneates a nonce consisting of a randomly selected 16-byte -// value that has been base64-encoded. -func generateNonce() (nonce []byte) { - key := make([]byte, 16) - if _, err := io.ReadFull(rand.Reader, key); err != nil { - panic(err) - } - nonce = make([]byte, 24) - base64.StdEncoding.Encode(nonce, key) - return -} - -// getNonceAccept computes the base64-encoded SHA-1 of the concatenation of -// the nonce ("Sec-WebSocket-Key" value) with the websocket GUID string. -func getNonceAccept(nonce []byte) (expected []byte, err error) { - h := sha1.New() - if _, err = h.Write(nonce); err != nil { - return - } - if _, err = h.Write([]byte(websocketGUID)); err != nil { - return - } - expected = make([]byte, 28) - base64.StdEncoding.Encode(expected, h.Sum(nil)) - return -} - -func isHybiVersion(version int) bool { - switch version { - case ProtocolVersionHybi08, ProtocolVersionHybi13: - return true - default: - } - return false -} - -// Client handhake described in draft-ietf-hybi-thewebsocket-protocol-17 -func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) { - if !isHybiVersion(config.Version) { - panic("wrong protocol version.") - } - - bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n") - - bw.WriteString("Host: " + config.Location.Host + "\r\n") - bw.WriteString("Upgrade: websocket\r\n") - bw.WriteString("Connection: Upgrade\r\n") - nonce := generateNonce() - if config.handshakeData != nil { - nonce = []byte(config.handshakeData["key"]) - } - bw.WriteString("Sec-WebSocket-Key: " + string(nonce) + "\r\n") - if config.Version == ProtocolVersionHybi13 { - bw.WriteString("Origin: " + strings.ToLower(config.Origin.String()) + "\r\n") - } else if config.Version == ProtocolVersionHybi08 { - bw.WriteString("Sec-WebSocket-Origin: " + strings.ToLower(config.Origin.String()) + "\r\n") - } - bw.WriteString("Sec-WebSocket-Version: " + fmt.Sprintf("%d", config.Version) + "\r\n") - if len(config.Protocol) > 0 { - bw.WriteString("Sec-WebSocket-Protocol: " + strings.Join(config.Protocol, ", ") + "\r\n") - } - // TODO(ukai): send extensions. - // TODO(ukai): send cookie if any. - - bw.WriteString("\r\n") - if err = bw.Flush(); err != nil { - return err - } - - resp, err := http.ReadResponse(br, &http.Request{Method: "GET"}) - if err != nil { - return err - } - if resp.StatusCode != 101 { - return ErrBadStatus - } - if strings.ToLower(resp.Header.Get("Upgrade")) != "websocket" || - strings.ToLower(resp.Header.Get("Connection")) != "upgrade" { - return ErrBadUpgrade - } - expectedAccept, err := getNonceAccept(nonce) - if err != nil { - return err - } - if resp.Header.Get("Sec-WebSocket-Accept") != string(expectedAccept) { - return ErrChallengeResponse - } - if resp.Header.Get("Sec-WebSocket-Extensions") != "" { - return ErrUnsupportedExtensions - } - offeredProtocol := resp.Header.Get("Sec-WebSocket-Protocol") - if offeredProtocol != "" { - protocolMatched := false - for i := 0; i < len(config.Protocol); i++ { - if config.Protocol[i] == offeredProtocol { - protocolMatched = true - break - } - } - if !protocolMatched { - return ErrBadWebSocketProtocol - } - config.Protocol = []string{offeredProtocol} - } - - return nil -} - -// newHybiClientConn creates a client WebSocket connection after handshake. -func newHybiClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn { - return newHybiConn(config, buf, rwc, nil) -} - -// A HybiServerHandshaker performs a server handshake using hybi draft protocol. -type hybiServerHandshaker struct { - *Config - accept []byte -} - -func (c *hybiServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) { - c.Version = ProtocolVersionHybi13 - if req.Method != "GET" { - return http.StatusMethodNotAllowed, ErrBadRequestMethod - } - // HTTP version can be safely ignored. - - if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" || - !strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") { - return http.StatusBadRequest, ErrNotWebSocket - } - - key := req.Header.Get("Sec-Websocket-Key") - if key == "" { - return http.StatusBadRequest, ErrChallengeResponse - } - version := req.Header.Get("Sec-Websocket-Version") - var origin string - switch version { - case "13": - c.Version = ProtocolVersionHybi13 - origin = req.Header.Get("Origin") - case "8": - c.Version = ProtocolVersionHybi08 - origin = req.Header.Get("Sec-Websocket-Origin") - default: - return http.StatusBadRequest, ErrBadWebSocketVersion - } - c.Origin, err = url.ParseRequestURI(origin) - if err != nil { - return http.StatusForbidden, err - } - var scheme string - if req.TLS != nil { - scheme = "wss" - } else { - scheme = "ws" - } - c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI()) - if err != nil { - return http.StatusBadRequest, err - } - protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol")) - protocols := strings.Split(protocol, ",") - for i := 0; i < len(protocols); i++ { - c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i])) - } - c.accept, err = getNonceAccept([]byte(key)) - if err != nil { - return http.StatusInternalServerError, err - } - return http.StatusSwitchingProtocols, nil -} - -func (c *hybiServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) { - if len(c.Protocol) > 0 { - if len(c.Protocol) != 1 { - return ErrBadWebSocketProtocol - } - } - buf.WriteString("HTTP/1.1 101 Switching Protocols\r\n") - buf.WriteString("Upgrade: websocket\r\n") - buf.WriteString("Connection: Upgrade\r\n") - buf.WriteString("Sec-WebSocket-Accept: " + string(c.accept) + "\r\n") - if len(c.Protocol) > 0 { - buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n") - } - // TODO(ukai): support extensions - buf.WriteString("\r\n") - return buf.Flush() -} - -func (c *hybiServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { - return newHybiServerConn(c.Config, buf, rwc, request) -} - -// newHybiServerConn returns a new WebSocket connection speaking hybi draft protocol. -func newHybiServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { - return newHybiConn(config, buf, rwc, request) -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hybi_test.go b/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hybi_test.go deleted file mode 100644 index 7976054..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hybi_test.go +++ /dev/null @@ -1,584 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bufio" - "bytes" - "fmt" - "io" - "net/http" - "net/url" - "strings" - "testing" -) - -// Test the getNonceAccept function with values in -// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 -func TestSecWebSocketAccept(t *testing.T) { - nonce := []byte("dGhlIHNhbXBsZSBub25jZQ==") - expected := []byte("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=") - accept, err := getNonceAccept(nonce) - if err != nil { - t.Errorf("getNonceAccept: returned error %v", err) - return - } - if !bytes.Equal(expected, accept) { - t.Errorf("getNonceAccept: expected %q got %q", expected, accept) - } -} - -func TestHybiClientHandshake(t *testing.T) { - b := bytes.NewBuffer([]byte{}) - bw := bufio.NewWriter(b) - br := bufio.NewReader(strings.NewReader(`HTTP/1.1 101 Switching Protocols -Upgrade: websocket -Connection: Upgrade -Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= -Sec-WebSocket-Protocol: chat - -`)) - var err error - config := new(Config) - config.Location, err = url.ParseRequestURI("ws://server.example.com/chat") - if err != nil { - t.Fatal("location url", err) - } - config.Origin, err = url.ParseRequestURI("http://example.com") - if err != nil { - t.Fatal("origin url", err) - } - config.Protocol = append(config.Protocol, "chat") - config.Protocol = append(config.Protocol, "superchat") - config.Version = ProtocolVersionHybi13 - - config.handshakeData = map[string]string{ - "key": "dGhlIHNhbXBsZSBub25jZQ==", - } - err = hybiClientHandshake(config, br, bw) - if err != nil { - t.Errorf("handshake failed: %v", err) - } - req, err := http.ReadRequest(bufio.NewReader(b)) - if err != nil { - t.Fatalf("read request: %v", err) - } - if req.Method != "GET" { - t.Errorf("request method expected GET, but got %q", req.Method) - } - if req.URL.Path != "/chat" { - t.Errorf("request path expected /chat, but got %q", req.URL.Path) - } - if req.Proto != "HTTP/1.1" { - t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto) - } - if req.Host != "server.example.com" { - t.Errorf("request Host expected server.example.com, but got %v", req.Host) - } - var expectedHeader = map[string]string{ - "Connection": "Upgrade", - "Upgrade": "websocket", - "Sec-Websocket-Key": config.handshakeData["key"], - "Origin": config.Origin.String(), - "Sec-Websocket-Protocol": "chat, superchat", - "Sec-Websocket-Version": fmt.Sprintf("%d", ProtocolVersionHybi13), - } - for k, v := range expectedHeader { - if req.Header.Get(k) != v { - t.Errorf(fmt.Sprintf("%s expected %q but got %q", k, v, req.Header.Get(k))) - } - } -} - -func TestHybiClientHandshakeHybi08(t *testing.T) { - b := bytes.NewBuffer([]byte{}) - bw := bufio.NewWriter(b) - br := bufio.NewReader(strings.NewReader(`HTTP/1.1 101 Switching Protocols -Upgrade: websocket -Connection: Upgrade -Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= -Sec-WebSocket-Protocol: chat - -`)) - var err error - config := new(Config) - config.Location, err = url.ParseRequestURI("ws://server.example.com/chat") - if err != nil { - t.Fatal("location url", err) - } - config.Origin, err = url.ParseRequestURI("http://example.com") - if err != nil { - t.Fatal("origin url", err) - } - config.Protocol = append(config.Protocol, "chat") - config.Protocol = append(config.Protocol, "superchat") - config.Version = ProtocolVersionHybi08 - - config.handshakeData = map[string]string{ - "key": "dGhlIHNhbXBsZSBub25jZQ==", - } - err = hybiClientHandshake(config, br, bw) - if err != nil { - t.Errorf("handshake failed: %v", err) - } - req, err := http.ReadRequest(bufio.NewReader(b)) - if err != nil { - t.Fatalf("read request: %v", err) - } - if req.Method != "GET" { - t.Errorf("request method expected GET, but got %q", req.Method) - } - if req.URL.Path != "/chat" { - t.Errorf("request path expected /demo, but got %q", req.URL.Path) - } - if req.Proto != "HTTP/1.1" { - t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto) - } - if req.Host != "server.example.com" { - t.Errorf("request Host expected example.com, but got %v", req.Host) - } - var expectedHeader = map[string]string{ - "Connection": "Upgrade", - "Upgrade": "websocket", - "Sec-Websocket-Key": config.handshakeData["key"], - "Sec-Websocket-Origin": config.Origin.String(), - "Sec-Websocket-Protocol": "chat, superchat", - "Sec-Websocket-Version": fmt.Sprintf("%d", ProtocolVersionHybi08), - } - for k, v := range expectedHeader { - if req.Header.Get(k) != v { - t.Errorf(fmt.Sprintf("%s expected %q but got %q", k, v, req.Header.Get(k))) - } - } -} - -func TestHybiServerHandshake(t *testing.T) { - config := new(Config) - handshaker := &hybiServerHandshaker{Config: config} - br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 -Host: server.example.com -Upgrade: websocket -Connection: Upgrade -Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== -Origin: http://example.com -Sec-WebSocket-Protocol: chat, superchat -Sec-WebSocket-Version: 13 - -`)) - req, err := http.ReadRequest(br) - if err != nil { - t.Fatal("request", err) - } - code, err := handshaker.ReadHandshake(br, req) - if err != nil { - t.Errorf("handshake failed: %v", err) - } - if code != http.StatusSwitchingProtocols { - t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code) - } - b := bytes.NewBuffer([]byte{}) - bw := bufio.NewWriter(b) - - config.Protocol = []string{"chat"} - - err = handshaker.AcceptHandshake(bw) - if err != nil { - t.Errorf("handshake response failed: %v", err) - } - expectedResponse := strings.Join([]string{ - "HTTP/1.1 101 Switching Protocols", - "Upgrade: websocket", - "Connection: Upgrade", - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", - "Sec-WebSocket-Protocol: chat", - "", ""}, "\r\n") - - if b.String() != expectedResponse { - t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) - } -} - -func TestHybiServerHandshakeHybi08(t *testing.T) { - config := new(Config) - handshaker := &hybiServerHandshaker{Config: config} - br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 -Host: server.example.com -Upgrade: websocket -Connection: Upgrade -Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== -Sec-WebSocket-Origin: http://example.com -Sec-WebSocket-Protocol: chat, superchat -Sec-WebSocket-Version: 8 - -`)) - req, err := http.ReadRequest(br) - if err != nil { - t.Fatal("request", err) - } - code, err := handshaker.ReadHandshake(br, req) - if err != nil { - t.Errorf("handshake failed: %v", err) - } - if code != http.StatusSwitchingProtocols { - t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code) - } - b := bytes.NewBuffer([]byte{}) - bw := bufio.NewWriter(b) - - config.Protocol = []string{"chat"} - - err = handshaker.AcceptHandshake(bw) - if err != nil { - t.Errorf("handshake response failed: %v", err) - } - expectedResponse := strings.Join([]string{ - "HTTP/1.1 101 Switching Protocols", - "Upgrade: websocket", - "Connection: Upgrade", - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", - "Sec-WebSocket-Protocol: chat", - "", ""}, "\r\n") - - if b.String() != expectedResponse { - t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) - } -} - -func TestHybiServerHandshakeHybiBadVersion(t *testing.T) { - config := new(Config) - handshaker := &hybiServerHandshaker{Config: config} - br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 -Host: server.example.com -Upgrade: websocket -Connection: Upgrade -Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== -Sec-WebSocket-Origin: http://example.com -Sec-WebSocket-Protocol: chat, superchat -Sec-WebSocket-Version: 9 - -`)) - req, err := http.ReadRequest(br) - if err != nil { - t.Fatal("request", err) - } - code, err := handshaker.ReadHandshake(br, req) - if err != ErrBadWebSocketVersion { - t.Errorf("handshake expected err %q but got %q", ErrBadWebSocketVersion, err) - } - if code != http.StatusBadRequest { - t.Errorf("status expected %q but got %q", http.StatusBadRequest, code) - } -} - -func testHybiFrame(t *testing.T, testHeader, testPayload, testMaskedPayload []byte, frameHeader *hybiFrameHeader) { - b := bytes.NewBuffer([]byte{}) - frameWriterFactory := &hybiFrameWriterFactory{bufio.NewWriter(b), false} - w, _ := frameWriterFactory.NewFrameWriter(TextFrame) - w.(*hybiFrameWriter).header = frameHeader - _, err := w.Write(testPayload) - w.Close() - if err != nil { - t.Errorf("Write error %q", err) - } - var expectedFrame []byte - expectedFrame = append(expectedFrame, testHeader...) - expectedFrame = append(expectedFrame, testMaskedPayload...) - if !bytes.Equal(expectedFrame, b.Bytes()) { - t.Errorf("frame expected %q got %q", expectedFrame, b.Bytes()) - } - frameReaderFactory := &hybiFrameReaderFactory{bufio.NewReader(b)} - r, err := frameReaderFactory.NewFrameReader() - if err != nil { - t.Errorf("Read error %q", err) - } - if header := r.HeaderReader(); header == nil { - t.Errorf("no header") - } else { - actualHeader := make([]byte, r.Len()) - n, err := header.Read(actualHeader) - if err != nil { - t.Errorf("Read header error %q", err) - } else { - if n < len(testHeader) { - t.Errorf("header too short %q got %q", testHeader, actualHeader[:n]) - } - if !bytes.Equal(testHeader, actualHeader[:n]) { - t.Errorf("header expected %q got %q", testHeader, actualHeader[:n]) - } - } - } - if trailer := r.TrailerReader(); trailer != nil { - t.Errorf("unexpected trailer %q", trailer) - } - frame := r.(*hybiFrameReader) - if frameHeader.Fin != frame.header.Fin || - frameHeader.OpCode != frame.header.OpCode || - len(testPayload) != int(frame.header.Length) { - t.Errorf("mismatch %v (%d) vs %v", frameHeader, len(testPayload), frame) - } - payload := make([]byte, len(testPayload)) - _, err = r.Read(payload) - if err != nil { - t.Errorf("read %v", err) - } - if !bytes.Equal(testPayload, payload) { - t.Errorf("payload %q vs %q", testPayload, payload) - } -} - -func TestHybiShortTextFrame(t *testing.T) { - frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame} - payload := []byte("hello") - testHybiFrame(t, []byte{0x81, 0x05}, payload, payload, frameHeader) - - payload = make([]byte, 125) - testHybiFrame(t, []byte{0x81, 125}, payload, payload, frameHeader) -} - -func TestHybiShortMaskedTextFrame(t *testing.T) { - frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame, - MaskingKey: []byte{0xcc, 0x55, 0x80, 0x20}} - payload := []byte("hello") - maskedPayload := []byte{0xa4, 0x30, 0xec, 0x4c, 0xa3} - header := []byte{0x81, 0x85} - header = append(header, frameHeader.MaskingKey...) - testHybiFrame(t, header, payload, maskedPayload, frameHeader) -} - -func TestHybiShortBinaryFrame(t *testing.T) { - frameHeader := &hybiFrameHeader{Fin: true, OpCode: BinaryFrame} - payload := []byte("hello") - testHybiFrame(t, []byte{0x82, 0x05}, payload, payload, frameHeader) - - payload = make([]byte, 125) - testHybiFrame(t, []byte{0x82, 125}, payload, payload, frameHeader) -} - -func TestHybiControlFrame(t *testing.T) { - frameHeader := &hybiFrameHeader{Fin: true, OpCode: PingFrame} - payload := []byte("hello") - testHybiFrame(t, []byte{0x89, 0x05}, payload, payload, frameHeader) - - frameHeader = &hybiFrameHeader{Fin: true, OpCode: PongFrame} - testHybiFrame(t, []byte{0x8A, 0x05}, payload, payload, frameHeader) - - frameHeader = &hybiFrameHeader{Fin: true, OpCode: CloseFrame} - payload = []byte{0x03, 0xe8} // 1000 - testHybiFrame(t, []byte{0x88, 0x02}, payload, payload, frameHeader) -} - -func TestHybiLongFrame(t *testing.T) { - frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame} - payload := make([]byte, 126) - testHybiFrame(t, []byte{0x81, 126, 0x00, 126}, payload, payload, frameHeader) - - payload = make([]byte, 65535) - testHybiFrame(t, []byte{0x81, 126, 0xff, 0xff}, payload, payload, frameHeader) - - payload = make([]byte, 65536) - testHybiFrame(t, []byte{0x81, 127, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00}, payload, payload, frameHeader) -} - -func TestHybiClientRead(t *testing.T) { - wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o', - 0x89, 0x05, 'h', 'e', 'l', 'l', 'o', // ping - 0x81, 0x05, 'w', 'o', 'r', 'l', 'd'} - br := bufio.NewReader(bytes.NewBuffer(wireData)) - bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) - conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil) - - msg := make([]byte, 512) - n, err := conn.Read(msg) - if err != nil { - t.Errorf("read 1st frame, error %q", err) - } - if n != 5 { - t.Errorf("read 1st frame, expect 5, got %d", n) - } - if !bytes.Equal(wireData[2:7], msg[:n]) { - t.Errorf("read 1st frame %v, got %v", wireData[2:7], msg[:n]) - } - n, err = conn.Read(msg) - if err != nil { - t.Errorf("read 2nd frame, error %q", err) - } - if n != 5 { - t.Errorf("read 2nd frame, expect 5, got %d", n) - } - if !bytes.Equal(wireData[16:21], msg[:n]) { - t.Errorf("read 2nd frame %v, got %v", wireData[16:21], msg[:n]) - } - n, err = conn.Read(msg) - if err == nil { - t.Errorf("read not EOF") - } - if n != 0 { - t.Errorf("expect read 0, got %d", n) - } -} - -func TestHybiShortRead(t *testing.T) { - wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o', - 0x89, 0x05, 'h', 'e', 'l', 'l', 'o', // ping - 0x81, 0x05, 'w', 'o', 'r', 'l', 'd'} - br := bufio.NewReader(bytes.NewBuffer(wireData)) - bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) - conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil) - - step := 0 - pos := 0 - expectedPos := []int{2, 5, 16, 19} - expectedLen := []int{3, 2, 3, 2} - for { - msg := make([]byte, 3) - n, err := conn.Read(msg) - if step >= len(expectedPos) { - if err == nil { - t.Errorf("read not EOF") - } - if n != 0 { - t.Errorf("expect read 0, got %d", n) - } - return - } - pos = expectedPos[step] - endPos := pos + expectedLen[step] - if err != nil { - t.Errorf("read from %d, got error %q", pos, err) - return - } - if n != endPos-pos { - t.Errorf("read from %d, expect %d, got %d", pos, endPos-pos, n) - } - if !bytes.Equal(wireData[pos:endPos], msg[:n]) { - t.Errorf("read from %d, frame %v, got %v", pos, wireData[pos:endPos], msg[:n]) - } - step++ - } -} - -func TestHybiServerRead(t *testing.T) { - wireData := []byte{0x81, 0x85, 0xcc, 0x55, 0x80, 0x20, - 0xa4, 0x30, 0xec, 0x4c, 0xa3, // hello - 0x89, 0x85, 0xcc, 0x55, 0x80, 0x20, - 0xa4, 0x30, 0xec, 0x4c, 0xa3, // ping: hello - 0x81, 0x85, 0xed, 0x83, 0xb4, 0x24, - 0x9a, 0xec, 0xc6, 0x48, 0x89, // world - } - br := bufio.NewReader(bytes.NewBuffer(wireData)) - bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) - conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, new(http.Request)) - - expected := [][]byte{[]byte("hello"), []byte("world")} - - msg := make([]byte, 512) - n, err := conn.Read(msg) - if err != nil { - t.Errorf("read 1st frame, error %q", err) - } - if n != 5 { - t.Errorf("read 1st frame, expect 5, got %d", n) - } - if !bytes.Equal(expected[0], msg[:n]) { - t.Errorf("read 1st frame %q, got %q", expected[0], msg[:n]) - } - - n, err = conn.Read(msg) - if err != nil { - t.Errorf("read 2nd frame, error %q", err) - } - if n != 5 { - t.Errorf("read 2nd frame, expect 5, got %d", n) - } - if !bytes.Equal(expected[1], msg[:n]) { - t.Errorf("read 2nd frame %q, got %q", expected[1], msg[:n]) - } - - n, err = conn.Read(msg) - if err == nil { - t.Errorf("read not EOF") - } - if n != 0 { - t.Errorf("expect read 0, got %d", n) - } -} - -func TestHybiServerReadWithoutMasking(t *testing.T) { - wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o'} - br := bufio.NewReader(bytes.NewBuffer(wireData)) - bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) - conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, new(http.Request)) - // server MUST close the connection upon receiving a non-masked frame. - msg := make([]byte, 512) - _, err := conn.Read(msg) - if err != io.EOF { - t.Errorf("read 1st frame, expect %q, but got %q", io.EOF, err) - } -} - -func TestHybiClientReadWithMasking(t *testing.T) { - wireData := []byte{0x81, 0x85, 0xcc, 0x55, 0x80, 0x20, - 0xa4, 0x30, 0xec, 0x4c, 0xa3, // hello - } - br := bufio.NewReader(bytes.NewBuffer(wireData)) - bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) - conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil) - - // client MUST close the connection upon receiving a masked frame. - msg := make([]byte, 512) - _, err := conn.Read(msg) - if err != io.EOF { - t.Errorf("read 1st frame, expect %q, but got %q", io.EOF, err) - } -} - -// Test the hybiServerHandshaker supports firefox implementation and -// checks Connection request header include (but it's not necessary -// equal to) "upgrade" -func TestHybiServerFirefoxHandshake(t *testing.T) { - config := new(Config) - handshaker := &hybiServerHandshaker{Config: config} - br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 -Host: server.example.com -Upgrade: websocket -Connection: keep-alive, upgrade -Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== -Origin: http://example.com -Sec-WebSocket-Protocol: chat, superchat -Sec-WebSocket-Version: 13 - -`)) - req, err := http.ReadRequest(br) - if err != nil { - t.Fatal("request", err) - } - code, err := handshaker.ReadHandshake(br, req) - if err != nil { - t.Errorf("handshake failed: %v", err) - } - if code != http.StatusSwitchingProtocols { - t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code) - } - b := bytes.NewBuffer([]byte{}) - bw := bufio.NewWriter(b) - - config.Protocol = []string{"chat"} - - err = handshaker.AcceptHandshake(bw) - if err != nil { - t.Errorf("handshake response failed: %v", err) - } - expectedResponse := strings.Join([]string{ - "HTTP/1.1 101 Switching Protocols", - "Upgrade: websocket", - "Connection: Upgrade", - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", - "Sec-WebSocket-Protocol: chat", - "", ""}, "\r\n") - - if b.String() != expectedResponse { - t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) - } -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/server.go b/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/server.go deleted file mode 100644 index 63f48e2..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/server.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bufio" - "fmt" - "io" - "net/http" -) - -func newServerConn(rwc io.ReadWriteCloser, buf *bufio.ReadWriter, req *http.Request) (conn *Conn, err error) { - config := new(Config) - var hs serverHandshaker = &hybiServerHandshaker{Config: config} - code, err := hs.ReadHandshake(buf.Reader, req) - if err == ErrBadWebSocketVersion { - fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) - fmt.Fprintf(buf, "Sec-WebSocket-Version: %s\r\n", SupportedProtocolVersion) - buf.WriteString("\r\n") - buf.WriteString(err.Error()) - buf.Flush() - return - } - if err != nil { - hs = &hixie76ServerHandshaker{Config: config} - code, err = hs.ReadHandshake(buf.Reader, req) - } - if err != nil { - hs = &hixie75ServerHandshaker{Config: config} - code, err = hs.ReadHandshake(buf.Reader, req) - } - if err != nil { - fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) - buf.WriteString("\r\n") - buf.WriteString(err.Error()) - buf.Flush() - return - } - config.Protocol = nil - - err = hs.AcceptHandshake(buf.Writer) - if err != nil { - code = http.StatusBadRequest - fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) - buf.WriteString("\r\n") - buf.Flush() - return - } - conn = hs.NewServerConn(buf, rwc, req) - return -} - -/* -Handler is an interface to a WebSocket. - -A trivial example server: - - package main - - import ( - "io" - "net/http" - "websocket" - ) - - // Echo the data received on the WebSocket. - func EchoServer(ws *websocket.Conn) { - io.Copy(ws, ws); - } - - func main() { - http.Handle("/echo", websocket.Handler(EchoServer)); - err := http.ListenAndServe(":12345", nil); - if err != nil { - panic("ListenAndServe: " + err.Error()) - } - } -*/ -type Handler func(*Conn) - -// ServeHTTP implements the http.Handler interface for a Web Socket -func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - rwc, buf, err := w.(http.Hijacker).Hijack() - if err != nil { - panic("Hijack failed: " + err.Error()) - return - } - // The server should abort the WebSocket connection if it finds - // the client did not send a handshake that matches with protocol - // specification. - defer rwc.Close() - conn, err := newServerConn(rwc, buf, req) - if err != nil { - return - } - if conn == nil { - panic("unepxected nil conn") - } - h(conn) -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/websocket.go b/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/websocket.go deleted file mode 100644 index f7aabc9..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/websocket.go +++ /dev/null @@ -1,412 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package websocket implements a client and server for the WebSocket protocol. -// The protocol is defined at http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol -package websocket - -import ( - "bufio" - "crypto/tls" - "encoding/json" - "errors" - "io" - "io/ioutil" - "net" - "net/http" - "net/url" - "sync" - "time" -) - -const ( - ProtocolVersionHixie75 = -75 - ProtocolVersionHixie76 = -76 - ProtocolVersionHybi00 = 0 - ProtocolVersionHybi08 = 8 - ProtocolVersionHybi13 = 13 - ProtocolVersionHybi = ProtocolVersionHybi13 - SupportedProtocolVersion = "13, 8" - - ContinuationFrame = 0 - TextFrame = 1 - BinaryFrame = 2 - CloseFrame = 8 - PingFrame = 9 - PongFrame = 10 - UnknownFrame = 255 -) - -// WebSocket protocol errors. -type ProtocolError struct { - ErrorString string -} - -func (err *ProtocolError) Error() string { return err.ErrorString } - -var ( - ErrBadProtocolVersion = &ProtocolError{"bad protocol version"} - ErrBadScheme = &ProtocolError{"bad scheme"} - ErrBadStatus = &ProtocolError{"bad status"} - ErrBadUpgrade = &ProtocolError{"missing or bad upgrade"} - ErrBadWebSocketOrigin = &ProtocolError{"missing or bad WebSocket-Origin"} - ErrBadWebSocketLocation = &ProtocolError{"missing or bad WebSocket-Location"} - ErrBadWebSocketProtocol = &ProtocolError{"missing or bad WebSocket-Protocol"} - ErrBadWebSocketVersion = &ProtocolError{"missing or bad WebSocket Version"} - ErrChallengeResponse = &ProtocolError{"mismatch challenge/response"} - ErrBadFrame = &ProtocolError{"bad frame"} - ErrBadFrameBoundary = &ProtocolError{"not on frame boundary"} - ErrNotWebSocket = &ProtocolError{"not websocket protocol"} - ErrBadRequestMethod = &ProtocolError{"bad method"} - ErrNotSupported = &ProtocolError{"not supported"} -) - -// Addr is an implementation of net.Addr for WebSocket. -type Addr struct { - *url.URL -} - -// Network returns the network type for a WebSocket, "websocket". -func (addr *Addr) Network() string { return "websocket" } - -// Config is a WebSocket configuration -type Config struct { - // A WebSocket server address. - Location *url.URL - - // A Websocket client origin. - Origin *url.URL - - // WebSocket subprotocols. - Protocol []string - - // WebSocket protocol version. - Version int - - // TLS config for secure WebSocket (wss). - TlsConfig *tls.Config - - handshakeData map[string]string -} - -// serverHandshaker is an interface to handle WebSocket server side handshake. -type serverHandshaker interface { - // ReadHandshake reads handshake request message from client. - // Returns http response code and error if any. - ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) - - // AcceptHandshake accepts the client handshake request and sends - // handshake response back to client. - AcceptHandshake(buf *bufio.Writer) (err error) - - // NewServerConn creates a new WebSocket connection. - NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) -} - -// frameReader is an interface to read a WebSocket frame. -type frameReader interface { - // Reader is to read payload of the frame. - io.Reader - - // PayloadType returns payload type. - PayloadType() byte - - // HeaderReader returns a reader to read header of the frame. - HeaderReader() io.Reader - - // TrailerReader returns a reader to read trailer of the frame. - // If it returns nil, there is no trailer in the frame. - TrailerReader() io.Reader - - // Len returns total length of the frame, including header and trailer. - Len() int -} - -// frameReaderFactory is an interface to creates new frame reader. -type frameReaderFactory interface { - NewFrameReader() (r frameReader, err error) -} - -// frameWriter is an interface to write a WebSocket frame. -type frameWriter interface { - // Writer is to write playload of the frame. - io.WriteCloser -} - -// frameWriterFactory is an interface to create new frame writer. -type frameWriterFactory interface { - NewFrameWriter(payloadType byte) (w frameWriter, err error) -} - -type frameHandler interface { - HandleFrame(frame frameReader) (r frameReader, err error) - WriteClose(status int) (err error) -} - -// Conn represents a WebSocket connection. -type Conn struct { - config *Config - request *http.Request - - buf *bufio.ReadWriter - rwc io.ReadWriteCloser - - rio sync.Mutex - frameReaderFactory - frameReader - - wio sync.Mutex - frameWriterFactory - - frameHandler - PayloadType byte - defaultCloseStatus int -} - -// Read implements the io.Reader interface: -// it reads data of a frame from the WebSocket connection. -// if msg is not large enough for the frame data, it fills the msg and next Read -// will read the rest of the frame data. -// it reads Text frame or Binary frame. -func (ws *Conn) Read(msg []byte) (n int, err error) { - ws.rio.Lock() - defer ws.rio.Unlock() -again: - if ws.frameReader == nil { - frame, err := ws.frameReaderFactory.NewFrameReader() - if err != nil { - return 0, err - } - ws.frameReader, err = ws.frameHandler.HandleFrame(frame) - if err != nil { - return 0, err - } - if ws.frameReader == nil { - goto again - } - } - n, err = ws.frameReader.Read(msg) - if err == io.EOF { - if trailer := ws.frameReader.TrailerReader(); trailer != nil { - io.Copy(ioutil.Discard, trailer) - } - ws.frameReader = nil - goto again - } - return n, err -} - -// Write implements the io.Writer interface: -// it writes data as a frame to the WebSocket connection. -func (ws *Conn) Write(msg []byte) (n int, err error) { - ws.wio.Lock() - defer ws.wio.Unlock() - w, err := ws.frameWriterFactory.NewFrameWriter(ws.PayloadType) - if err != nil { - return 0, err - } - n, err = w.Write(msg) - w.Close() - if err != nil { - return n, err - } - return n, err -} - -// Close implements the io.Closer interface. -func (ws *Conn) Close() error { - err := ws.frameHandler.WriteClose(ws.defaultCloseStatus) - if err != nil { - return err - } - return ws.rwc.Close() -} - -func (ws *Conn) IsClientConn() bool { return ws.request == nil } -func (ws *Conn) IsServerConn() bool { return ws.request != nil } - -// LocalAddr returns the WebSocket Origin for the connection for client, or -// the WebSocket location for server. -func (ws *Conn) LocalAddr() net.Addr { - if ws.IsClientConn() { - return &Addr{ws.config.Origin} - } - return &Addr{ws.config.Location} -} - -// RemoteAddr returns the WebSocket location for the connection for client, or -// the Websocket Origin for server. -func (ws *Conn) RemoteAddr() net.Addr { - if ws.IsClientConn() { - return &Addr{ws.config.Location} - } - return &Addr{ws.config.Origin} -} - -var errSetDeadline = errors.New("websocket: cannot set deadline: not using a net.Conn") - -// SetDeadline sets the connection's network read & write deadlines. -func (ws *Conn) SetDeadline(t time.Time) error { - if conn, ok := ws.rwc.(net.Conn); ok { - return conn.SetDeadline(t) - } - return errSetDeadline -} - -// SetReadDeadline sets the connection's network read deadline. -func (ws *Conn) SetReadDeadline(t time.Time) error { - if conn, ok := ws.rwc.(net.Conn); ok { - return conn.SetReadDeadline(t) - } - return errSetDeadline -} - -// SetWriteDeadline sets the connection's network write deadline. -func (ws *Conn) SetWriteDeadline(t time.Time) error { - if conn, ok := ws.rwc.(net.Conn); ok { - return conn.SetWriteDeadline(t) - } - return errSetDeadline -} - -// Config returns the WebSocket config. -func (ws *Conn) Config() *Config { return ws.config } - -// Request returns the http request upgraded to the WebSocket. -// It is nil for client side. -func (ws *Conn) Request() *http.Request { return ws.request } - -// Codec represents a symmetric pair of functions that implement a codec. -type Codec struct { - Marshal func(v interface{}) (data []byte, payloadType byte, err error) - Unmarshal func(data []byte, payloadType byte, v interface{}) (err error) -} - -// Send sends v marshaled by cd.Marshal as single frame to ws. -func (cd Codec) Send(ws *Conn, v interface{}) (err error) { - if err != nil { - return err - } - data, payloadType, err := cd.Marshal(v) - if err != nil { - return err - } - ws.wio.Lock() - defer ws.wio.Unlock() - w, err := ws.frameWriterFactory.NewFrameWriter(payloadType) - _, err = w.Write(data) - w.Close() - return err -} - -// Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores in v. -func (cd Codec) Receive(ws *Conn, v interface{}) (err error) { - ws.rio.Lock() - defer ws.rio.Unlock() - if ws.frameReader != nil { - _, err = io.Copy(ioutil.Discard, ws.frameReader) - if err != nil { - return err - } - ws.frameReader = nil - } -again: - frame, err := ws.frameReaderFactory.NewFrameReader() - if err != nil { - return err - } - frame, err = ws.frameHandler.HandleFrame(frame) - if err != nil { - return err - } - if frame == nil { - goto again - } - payloadType := frame.PayloadType() - data, err := ioutil.ReadAll(frame) - if err != nil { - return err - } - return cd.Unmarshal(data, payloadType, v) -} - -func marshal(v interface{}) (msg []byte, payloadType byte, err error) { - switch data := v.(type) { - case string: - return []byte(data), TextFrame, nil - case []byte: - return data, BinaryFrame, nil - } - return nil, UnknownFrame, ErrNotSupported -} - -func unmarshal(msg []byte, payloadType byte, v interface{}) (err error) { - switch data := v.(type) { - case *string: - *data = string(msg) - return nil - case *[]byte: - *data = msg - return nil - } - return ErrNotSupported -} - -/* -Message is a codec to send/receive text/binary data in a frame on WebSocket connection. -To send/receive text frame, use string type. -To send/receive binary frame, use []byte type. - -Trivial usage: - - import "websocket" - - // receive text frame - var message string - websocket.Message.Receive(ws, &message) - - // send text frame - message = "hello" - websocket.Message.Send(ws, message) - - // receive binary frame - var data []byte - websocket.Message.Receive(ws, &data) - - // send binary frame - data = []byte{0, 1, 2} - websocket.Message.Send(ws, data) - -*/ -var Message = Codec{marshal, unmarshal} - -func jsonMarshal(v interface{}) (msg []byte, payloadType byte, err error) { - msg, err = json.Marshal(v) - return msg, TextFrame, err -} - -func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err error) { - return json.Unmarshal(msg, v) -} - -/* -JSON is a codec to send/receive JSON data in a frame from a WebSocket connection. - -Trival usage: - - import "websocket" - - type T struct { - Msg string - Count int - } - - // receive JSON type T - var data T - websocket.JSON.Receive(ws, &data) - - // send JSON type T - websocket.JSON.Send(ws, data) -*/ -var JSON = Codec{jsonMarshal, jsonUnmarshal} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/websocket_test.go b/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/websocket_test.go deleted file mode 100644 index 27c58a0..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/websocket_test.go +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bytes" - "fmt" - "io" - "log" - "net" - "net/http" - "net/http/httptest" - "net/url" - "strings" - "sync" - "testing" -) - -var serverAddr string -var once sync.Once - -func echoServer(ws *Conn) { io.Copy(ws, ws) } - -type Count struct { - S string - N int -} - -func countServer(ws *Conn) { - for { - var count Count - err := JSON.Receive(ws, &count) - if err != nil { - return - } - count.N++ - count.S = strings.Repeat(count.S, count.N) - err = JSON.Send(ws, count) - if err != nil { - return - } - } -} - -func startServer() { - http.Handle("/echo", Handler(echoServer)) - http.Handle("/count", Handler(countServer)) - server := httptest.NewServer(nil) - serverAddr = server.Listener.Addr().String() - log.Print("Test WebSocket server listening on ", serverAddr) -} - -func newConfig(t *testing.T, path string) *Config { - config, _ := NewConfig(fmt.Sprintf("ws://%s%s", serverAddr, path), "http://localhost") - return config -} - -func TestEcho(t *testing.T) { - once.Do(startServer) - - // websocket.Dial() - client, err := net.Dial("tcp", serverAddr) - if err != nil { - t.Fatal("dialing", err) - } - conn, err := NewClient(newConfig(t, "/echo"), client) - if err != nil { - t.Errorf("WebSocket handshake error: %v", err) - return - } - - msg := []byte("hello, world\n") - if _, err := conn.Write(msg); err != nil { - t.Errorf("Write: %v", err) - } - var actual_msg = make([]byte, 512) - n, err := conn.Read(actual_msg) - if err != nil { - t.Errorf("Read: %v", err) - } - actual_msg = actual_msg[0:n] - if !bytes.Equal(msg, actual_msg) { - t.Errorf("Echo: expected %q got %q", msg, actual_msg) - } - conn.Close() -} - -func TestAddr(t *testing.T) { - once.Do(startServer) - - // websocket.Dial() - client, err := net.Dial("tcp", serverAddr) - if err != nil { - t.Fatal("dialing", err) - } - conn, err := NewClient(newConfig(t, "/echo"), client) - if err != nil { - t.Errorf("WebSocket handshake error: %v", err) - return - } - - ra := conn.RemoteAddr().String() - if !strings.HasPrefix(ra, "ws://") || !strings.HasSuffix(ra, "/echo") { - t.Errorf("Bad remote addr: %v", ra) - } - la := conn.LocalAddr().String() - if !strings.HasPrefix(la, "http://") { - t.Errorf("Bad local addr: %v", la) - } - conn.Close() -} - -func TestCount(t *testing.T) { - once.Do(startServer) - - // websocket.Dial() - client, err := net.Dial("tcp", serverAddr) - if err != nil { - t.Fatal("dialing", err) - } - conn, err := NewClient(newConfig(t, "/count"), client) - if err != nil { - t.Errorf("WebSocket handshake error: %v", err) - return - } - - var count Count - count.S = "hello" - if err := JSON.Send(conn, count); err != nil { - t.Errorf("Write: %v", err) - } - if err := JSON.Receive(conn, &count); err != nil { - t.Errorf("Read: %v", err) - } - if count.N != 1 { - t.Errorf("count: expected %d got %d", 1, count.N) - } - if count.S != "hello" { - t.Errorf("count: expected %q got %q", "hello", count.S) - } - if err := JSON.Send(conn, count); err != nil { - t.Errorf("Write: %v", err) - } - if err := JSON.Receive(conn, &count); err != nil { - t.Errorf("Read: %v", err) - } - if count.N != 2 { - t.Errorf("count: expected %d got %d", 2, count.N) - } - if count.S != "hellohello" { - t.Errorf("count: expected %q got %q", "hellohello", count.S) - } - conn.Close() -} - -func TestWithQuery(t *testing.T) { - once.Do(startServer) - - client, err := net.Dial("tcp", serverAddr) - if err != nil { - t.Fatal("dialing", err) - } - - config := newConfig(t, "/echo") - config.Location, err = url.ParseRequestURI(fmt.Sprintf("ws://%s/echo?q=v", serverAddr)) - if err != nil { - t.Fatal("location url", err) - } - - ws, err := NewClient(config, client) - if err != nil { - t.Errorf("WebSocket handshake: %v", err) - return - } - ws.Close() -} - -func TestWithProtocol(t *testing.T) { - once.Do(startServer) - - client, err := net.Dial("tcp", serverAddr) - if err != nil { - t.Fatal("dialing", err) - } - - config := newConfig(t, "/echo") - config.Protocol = append(config.Protocol, "test") - - ws, err := NewClient(config, client) - if err != nil { - t.Errorf("WebSocket handshake: %v", err) - return - } - ws.Close() -} - -func TestHTTP(t *testing.T) { - once.Do(startServer) - - // If the client did not send a handshake that matches the protocol - // specification, the server MUST return an HTTP respose with an - // appropriate error code (such as 400 Bad Request) - resp, err := http.Get(fmt.Sprintf("http://%s/echo", serverAddr)) - if err != nil { - t.Errorf("Get: error %#v", err) - return - } - if resp == nil { - t.Error("Get: resp is null") - return - } - if resp.StatusCode != http.StatusBadRequest { - t.Errorf("Get: expected %q got %q", http.StatusBadRequest, resp.StatusCode) - } -} - -func TestTrailingSpaces(t *testing.T) { - // http://code.google.com/p/go/issues/detail?id=955 - // The last runs of this create keys with trailing spaces that should not be - // generated by the client. - once.Do(startServer) - config := newConfig(t, "/echo") - for i := 0; i < 30; i++ { - // body - ws, err := DialConfig(config) - if err != nil { - t.Errorf("Dial #%d failed: %v", i, err) - break - } - ws.Close() - } -} - -func TestSmallBuffer(t *testing.T) { - // http://code.google.com/p/go/issues/detail?id=1145 - // Read should be able to handle reading a fragment of a frame. - once.Do(startServer) - - // websocket.Dial() - client, err := net.Dial("tcp", serverAddr) - if err != nil { - t.Fatal("dialing", err) - } - conn, err := NewClient(newConfig(t, "/echo"), client) - if err != nil { - t.Errorf("WebSocket handshake error: %v", err) - return - } - - msg := []byte("hello, world\n") - if _, err := conn.Write(msg); err != nil { - t.Errorf("Write: %v", err) - } - var small_msg = make([]byte, 8) - n, err := conn.Read(small_msg) - if err != nil { - t.Errorf("Read: %v", err) - } - if !bytes.Equal(msg[:len(small_msg)], small_msg) { - t.Errorf("Echo: expected %q got %q", msg[:len(small_msg)], small_msg) - } - var second_msg = make([]byte, len(msg)) - n, err = conn.Read(second_msg) - if err != nil { - t.Errorf("Read: %v", err) - } - second_msg = second_msg[0:n] - if !bytes.Equal(msg[len(small_msg):], second_msg) { - t.Errorf("Echo: expected %q got %q", msg[len(small_msg):], second_msg) - } - conn.Close() -} diff --git a/eBook/examples/chapter_15/dial.go b/eBook/examples/chapter_15/dial.go deleted file mode 100644 index 29754ab..0000000 --- a/eBook/examples/chapter_15/dial.go +++ /dev/null @@ -1,29 +0,0 @@ -// dial.go.go -package main - -import ( - "fmt" - "net" - "os" -) - -func main() { - conn, err:= net.Dial("tcp", "192.0.32.10:80") - checkConnection(conn, err) - - conn, err =net.Dial("udp", "192.0.32.10:80") - checkConnection(conn, err) - - conn, err =net.Dial("tcp", "[2620:0:2d0:200::10]:80") - checkConnection(conn, err) -} - -func checkConnection(conn net.Conn, err error) { - if err!= nil { - fmt.Printf("error %v connecting!") - os.Exit(1) - } - - fmt.Println("Connection is made with %v", conn) - -} diff --git a/eBook/examples/chapter_15/elaborated_webserver.go b/eBook/examples/chapter_15/elaborated_webserver.go deleted file mode 100644 index 60c17f2..0000000 --- a/eBook/examples/chapter_15/elaborated_webserver.go +++ /dev/null @@ -1,146 +0,0 @@ -//Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -package main - -import ( - "bytes" - "expvar" - "flag" - "fmt" - "net/http" - "io" - "log" - "os" - "strconv" -) - -// hello world, the web server -var helloRequests = expvar.NewInt("hello-requests") -// flags: -var webroot = flag.String("root", "/home/user", "web root directory") -// simple flag server -var booleanflag = flag.Bool("boolean", true, "another flag for testing") - -// Simple counter server. POSTing to it will set the value. -type Counter struct { - n int -} - -// a channel -type Chan chan int - -func main() { - flag.Parse() - http.Handle("/", http.HandlerFunc(Logger)) - http.Handle("/go/hello", http.HandlerFunc(HelloServer)) - // The counter is published as a variable directly. - ctr := new(Counter) - expvar.Publish("counter", ctr) - http.Handle("/counter", ctr) - // http.Handle("/go/", http.FileServer(http.Dir("/tmp"))) // uses the OS filesystem - http.Handle("/go/", http.StripPrefix("/go/", http.FileServer(http.Dir(*webroot)))) - http.Handle("/flags", http.HandlerFunc(FlagServer)) - http.Handle("/args", http.HandlerFunc(ArgServer)) - http.Handle("/chan", ChanCreate()) - http.Handle("/date", http.HandlerFunc(DateServer)) - err := http.ListenAndServe(":12345", nil) - if err != nil { - log.Panicln("ListenAndServe:", err) - } -} - -func Logger(w http.ResponseWriter, req *http.Request) { - log.Print(req.URL.String()) - w.WriteHeader(404) - w.Write([]byte("oops")) -} - -func HelloServer(w http.ResponseWriter, req *http.Request) { - helloRequests.Add(1) - io.WriteString(w, "hello, world!\n") -} - -// This makes Counter satisfy the expvar.Var interface, so we can export -// it directly. -func (ctr *Counter) String() string { return fmt.Sprintf("%d", ctr.n) } - -func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) { - switch req.Method { - case "GET": // increment n - ctr.n++ - case "POST": // set n to posted value - buf := new(bytes.Buffer) - io.Copy(buf, req.Body) - body := buf.String() - if n, err := strconv.Atoi(body); err != nil { - fmt.Fprintf(w, "bad POST: %v\nbody: [%v]\n", err, body) - } else { - ctr.n = n - fmt.Fprint(w, "counter reset\n") - } - } - fmt.Fprintf(w, "counter = %d\n", ctr.n) -} - -func FlagServer(w http.ResponseWriter, req *http.Request) { - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - fmt.Fprint(w, "Flags:\n") - flag.VisitAll(func(f *flag.Flag) { - if f.Value.String() != f.DefValue { - fmt.Fprintf(w, "%s = %s [default = %s]\n", f.Name, f.Value.String(), f.DefValue) - } else { - fmt.Fprintf(w, "%s = %s\n", f.Name, f.Value.String()) - } - }) -} - -// simple argument server -func ArgServer(w http.ResponseWriter, req *http.Request) { - for _, s := range os.Args { - fmt.Fprint(w, s, " ") - } -} - -func ChanCreate() Chan { - c := make(Chan) - go func(c Chan) { - for x := 0; ; x++ { - c <- x - } - }(c) - return c -} - -func (ch Chan) ServeHTTP(w http.ResponseWriter, req *http.Request) { - io.WriteString(w, fmt.Sprintf("channel send #%d\n", <-ch)) -} - -// exec a program, redirecting output -func DateServer(rw http.ResponseWriter, req *http.Request) { - rw.Header().Set("Content-Type", "text/plain; charset=utf-8") - r, w, err := os.Pipe() - if err != nil { - fmt.Fprintf(rw, "pipe: %s\n", err) - return - } - - p, err := os.StartProcess("/bin/date", []string{"date"}, &os.ProcAttr{Files: []*os.File{nil, w, w}}) - defer r.Close() - w.Close() - if err != nil { - fmt.Fprintf(rw, "fork/exec: %s\n", err) - return - } - defer p.Release() - io.Copy(rw, r) - wait, err := p.Wait() - if err != nil { - fmt.Fprintf(rw, "wait: %s\n", err) - return - } - if !wait.Exited() { - fmt.Fprintf(rw, "date: %v\n", wait) - return - } -} \ No newline at end of file diff --git a/eBook/examples/chapter_15/hello_world_webserver.go b/eBook/examples/chapter_15/hello_world_webserver.go deleted file mode 100644 index d2414e8..0000000 --- a/eBook/examples/chapter_15/hello_world_webserver.go +++ /dev/null @@ -1,24 +0,0 @@ -// hello_world_webserver.go -package main - -import ( - "fmt" - "net/http" - "log" -) - -func HelloServer(w http.ResponseWriter, req *http.Request) { - fmt.Println("Inside HelloServer handler") - //fmt.Fprint(w, "Hello, " + req.URL.Path[1:]) - fmt.Fprintf(w, "Hello, %s ", req.URL.Path[1:]) - // io.WriteString(w, "hello, world!\n") -} - -func main() { - http.HandleFunc("/", HelloServer) - err := http.ListenAndServe("localhost:8080", nil) - if err != nil { - log.Fatal("ListenAndServe: ", err.Error()) - } - // http.ListenAndServe(":8080", http.HandlerFunc(HelloServer)) -} diff --git a/eBook/examples/chapter_15/http_fetch.go b/eBook/examples/chapter_15/http_fetch.go deleted file mode 100644 index 8b66d62..0000000 --- a/eBook/examples/chapter_15/http_fetch.go +++ /dev/null @@ -1,23 +0,0 @@ -// httpfetch.go -package main - -import ( - "fmt" - "net/http" - "io/ioutil" - "log" -) - -func main() { - res, err := http.Get("http://www.google.com") - CheckError(err) - data, err := ioutil.ReadAll(res.Body) - CheckError(err) - fmt.Printf("Got: %q", string(data)) -} - -func CheckError(err error) { - if err != nil { - log.Fatalf("Get: %v", err) - } -} diff --git a/eBook/examples/chapter_15/pipeline1.go b/eBook/examples/chapter_15/pipeline1.go deleted file mode 100644 index 32d74ee..0000000 --- a/eBook/examples/chapter_15/pipeline1.go +++ /dev/null @@ -1,17 +0,0 @@ -// pipeline1.go -package main - -import ( - "text/template" - "os" -) - -func main() { - t := template.New("template test") - t = template.Must(t.Parse("This is just static text. \n{{\"This is pipeline data - because it is evaluated within the double braces.\"}} {{`So is this, but within reverse quotes.`}}\n")) - t.Execute(os.Stdout, nil) -} -/* -This is just static text. -This is pipeline data - because it is evaluated within the double braces. So is this, but within reverse quotes. -*/ \ No newline at end of file diff --git a/eBook/examples/chapter_15/poll_url.go b/eBook/examples/chapter_15/poll_url.go deleted file mode 100644 index 2efe5f2..0000000 --- a/eBook/examples/chapter_15/poll_url.go +++ /dev/null @@ -1,30 +0,0 @@ -// poll_url.go -package main - -import ( - "fmt" - "net/http" -) - -var urls = []string{ - "http://www.google.com/", - "http://golang.org/", - "http://blog.golang.org/", -} - -func main() { - // Execute an HTTP HEAD request for all url's - // and returns the HTTP status string or an error string. - for _, url := range urls { - resp, err := http.Head(url) - if err != nil { - fmt.Println("Error", url, err) - } - fmt.Print(url, ": ", resp.Status) - } -} -/* Output: -http://www.google.com/ : 302 Found -http://golang.org/ : 200 OK -http://blog.golang.org/ : 200 OK -*/ diff --git a/eBook/examples/chapter_15/predefined_functions.go b/eBook/examples/chapter_15/predefined_functions.go deleted file mode 100644 index ec8962e..0000000 --- a/eBook/examples/chapter_15/predefined_functions.go +++ /dev/null @@ -1,14 +0,0 @@ -// predefined_functions.go -package main - -import ( - "os" - "text/template" -) - -func main() { - t := template.New("test") - t = template.Must(t.Parse("{{with $x := `hello`}}{{printf `%s %s` $x `Mary`}}{{end}}!\n")) - t.Execute(os.Stdout, nil) -} -// hello Mary! \ No newline at end of file diff --git a/eBook/examples/chapter_15/robust_webserver.go b/eBook/examples/chapter_15/robust_webserver.go deleted file mode 100644 index 07501fe..0000000 --- a/eBook/examples/chapter_15/robust_webserver.go +++ /dev/null @@ -1,57 +0,0 @@ -// robust_webserver.go -package main - -import ( - "net/http" - "io" - "log" -) - -const form = `
- - -
` - -type HandleFnc func(http.ResponseWriter,*http.Request) - -/* handle a simple get request */ -func SimpleServer(w http.ResponseWriter, request *http.Request) { - io.WriteString(w, "

hello, world

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

hello, world

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

Editing {{.Title |html}}

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

{{.Title |html}}

- -

[edit]

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

%s

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