Skip to content

Commit

Permalink
Adding chapter 8
Browse files Browse the repository at this point in the history
  • Loading branch information
mattfarina committed Oct 13, 2015
1 parent 2fc6e8b commit 3a36cd4
Show file tree
Hide file tree
Showing 12 changed files with 458 additions and 0 deletions.
21 changes: 21 additions & 0 deletions chapter8/custom_client_timeout.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package main

import (
"fmt"
"io/ioutil"
"net/http"
"os"
"time"
)

func main() {
cc := &http.Client{Timeout: time.Second}
res, err := cc.Get("http://goinpracticebook.com")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
b, _ := ioutil.ReadAll(res.Body)
res.Body.Close()
fmt.Printf("%s", b)
}
12 changes: 12 additions & 0 deletions chapter8/custom_request_delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package main

import (
"fmt"
"net/http"
)

func main() {
req, _ := http.NewRequest("DELETE", "http://example.com/foo/bar", nil)
res, _ := http.DefaultClient.Do(req)
fmt.Printf("%s", res.Status)
}
61 changes: 61 additions & 0 deletions chapter8/get_custom_error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package main

import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
)

type Error struct {
HTTPCode int `json:"-"`
Code int `json:"code,omitempty"`
Message string `json:"message"`
}

func (e Error) Error() string {
fs := "HTTP: %d, Code: %d, Message: %s"
return fmt.Sprintf(fs, e.HTTPCode, e.Code, e.Message)
}

func get(u string) (*http.Response, error) {
res, err := http.Get(u)
if err != nil {
return res, err
}

if res.StatusCode < 200 || res.StatusCode >= 300 {
if res.Header.Get("Content-Type") != "application/json" {
sm := "Unknown error. HTTP status: %s"
return res, fmt.Errorf(sm, res.Status)
}
b, _ := ioutil.ReadAll(res.Body)
res.Body.Close()
var data struct {
Er Error `json:"error"`
}
err = json.Unmarshal(b, &data)
if err != nil {
sm := "Unable to parse json: %s. HTTP status: %s"
return res, fmt.Errorf(sm, err, res.Status)
}
data.Er.HTTPCode = res.StatusCode

return res, data.Er
}

return res, nil
}

func main() {
res, err := get("http://localhost:8080")
if err != nil {
fmt.Println(err)
os.Exit(1)
}

b, _ := ioutil.ReadAll(res.Body)
res.Body.Close()
fmt.Printf("%s", b)
}
21 changes: 21 additions & 0 deletions chapter8/get_semantic_version_api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package main

import (
"fmt"
"io/ioutil"
"net/http"
)

func main() {
ct := "applicaiton/vnd.mytodos.json; version=2.0"
req, _ := http.NewRequest("GET", "http://localhost:8080/test", nil)
req.Header.Set("Accept", ct)
res, _ := http.DefaultClient.Do(req)
if res.Header.Get("Content-Type") != ct {
fmt.Println("Unexpected content type returned")
return
}
b, _ := ioutil.ReadAll(res.Body)
res.Body.Close()
fmt.Printf("%s", b)
}
41 changes: 41 additions & 0 deletions chapter8/http_custom_error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package main

import (
"encoding/json"
"fmt"
"net/http"
)

type Error struct {
HTTPCode int `json:"-"`
Code int `json:"code,omitempty"`
Message string `json:"message"`
}

func JSONError(w http.ResponseWriter, e Error) {
data := struct {
Er Error `json:"error"`
}{e}
b, err := json.Marshal(data)
if err != nil {
http.Error(w, "Internal Server Error", 500)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(e.HTTPCode)
fmt.Fprint(w, string(b))
}

func diaplayError(w http.ResponseWriter, r *http.Request) {
e := Error{
HTTPCode: http.StatusForbidden,
Code: 123,
Message: "An Error Occurred",
}
JSONError(w, e)
}

func main() {
http.HandleFunc("/", diaplayError)
http.ListenAndServe(":8080", nil)
}
12 changes: 12 additions & 0 deletions chapter8/http_error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package main

import "net/http"

func diaplayError(w http.ResponseWriter, r *http.Request) {
http.Error(w, "An Error Occurred", http.StatusForbidden)
}

func main() {
http.HandleFunc("/", diaplayError)
http.ListenAndServe(":8080", nil)
}
109 changes: 109 additions & 0 deletions chapter8/http_timeout_handling.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package main

import (
// "io"
"fmt"
"io"
"net"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"
)

func main() {
file, err := os.Create("ff.dmg")
if err != nil {
fmt.Println(err)
return
}
defer file.Close()

location := "https://download-installer.cdn.mozilla.net/pub/firefox/releases/40.0.3/mac/en-US/Firefox%2040.0.3.dmg"
err = download(location, file, 100)
if err != nil {
fmt.Println(err)
return
}

fi, err := file.Stat()
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Got it with %v bytes downloaded", fi.Size())
}

func download(location string, file *os.File, retries int64) error {
req, err := http.NewRequest("GET", location, nil)
if err != nil {
return err
}
fi, err := file.Stat()
if err != nil {
return err
}
current := fi.Size()
if current > 0 {
start := strconv.FormatInt(current, 10)
req.Header.Set("Range", "bytes="+start+"-")
}

cc := &http.Client{Timeout: 5 * time.Minute}
res, err := cc.Do(req)
if err != nil && hasTimedOut(err) {
if retries > 0 {
return download(location, file, retries-1)
}
return err
} else if err != nil {
return err
}

if res.StatusCode < 200 || res.StatusCode >= 300 {
errFmt := "Unsuccess HTTP request. Status: %s"
return fmt.Errorf(errFmt, res.Status)
}

if res.Header.Get("Accept-Ranges") != "bytes" {
retries = 0
}

_, err = io.Copy(file, res.Body)
if err != nil && hasTimedOut(err) {
if retries > 0 {
return download(location, file, retries-1)
}
return err
} else if err != nil {
return err
}

return nil
}

func hasTimedOut(err error) bool {
switch err := err.(type) {
case *url.Error:
if err, ok := err.Err.(net.Error); ok && err.Timeout() {
return true
}
case net.Error:
if err.Timeout() {
return true
}
case *net.OpError:
if err.Timeout() {
return true
}
}

errTxt := "use of closed network connection"
if err != nil && strings.Contains(err.Error(), errTxt) {
return true
}

return false
}
69 changes: 69 additions & 0 deletions chapter8/parse_arbitrary_json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package main

import (
"encoding/json"
"fmt"
"os"
)

var ks = []byte(`{
"firstName": "Jean",
"lastName": "Bartik",
"age": 86,
"education": [
{
"institution": "Northwest Missouri State Teachers College",
"degree": "Bachelor of Science in Mathematics"
},
{
"institution": "University of Pennsylvania",
"degree": "Masters in English"
}
],
"spouse": "William Bartik",
"children": [
"Timothy John Bartik",
"Jane Helen Bartik",
"Mary Ruth Bartik"
]
}`)

func main() {
var f interface{}
err := json.Unmarshal(ks, &f)
if err != nil {
fmt.Println(err)
os.Exit(1)
}

fmt.Println(f)

m := f.(map[string]interface{})
fmt.Println(m["firstName"])

fmt.Print("interface{} ")
printJSON(f)
}

func printJSON(v interface{}) {
switch vv := v.(type) {
case string:
fmt.Println("is string", vv)
case float64:
fmt.Println("is float64", vv)
case []interface{}:
fmt.Println("is an array:")
for i, u := range vv {
fmt.Print(i, " ")
printJSON(u)
}
case map[string]interface{}:
fmt.Println("is an object:")
for i, u := range vv {
fmt.Print(i, " ")
printJSON(u)
}
default:
fmt.Println("Unknown type")
}
}
Loading

0 comments on commit 3a36cd4

Please sign in to comment.