<commit_before>package ranger

import (
	"errors"
	"fmt"
	"io"
	"mime"
	"mime/multipart"
	"net/http"
	"net/url"
	"strings"
)

// HTTPClient is an interface describing the methods required from net/http.Client
type HTTPClient interface {
	Do(*http.Request) (*http.Response, error)
	Get(string) (*http.Response, error)
	Head(string) (*http.Response, error)
}

// HTTPRanger is a RangeFetcher that uses the HTTP Range: header to fetch blocks.
//
// HTTPRanger first makes a HEAD request and then between 0 and Length()/BlockSize GET requests, attempting
// whenever possible to optimize for a lower number of requests.
//
// No network requests are made until the first I/O-related function call.
type HTTPRanger struct {
	URL    *url.URL
	Client HTTPClient

	validator string
	length    int64
	blockSize int
}

func statusCodeError(status int) error {
	return fmt.Errorf("unexpected response (status %d)", status)
}

func statusIsAcceptable(status int) bool {
	return status >= 200 && status < 300
}

func validatorFromResponse(resp *http.Response) (string, error) {
	etag := resp.Header.Get("ETag")
	if etag != "" && etag[0] == '"' {
		return etag, nil
	}

	modtime := resp.Header.Get("Last-Modified")
	if modtime != "" {
		return modtime, nil
	}

	return "", errors.New("no applicable validator in response")
}

// Initialize implements the Initialize function from the RangeFetcher interface.
// It performs a HEAD request to retrieve the required information from the server.
func (r *HTTPRanger) Initialize(bs int) error {
	if r.Client == nil {
		r.Client = &http.Client{}
	}

	resp, err := r.Client.Head(r.URL.String())
	if err != nil {
		return err
	}

	if !statusIsAcceptable(resp.StatusCode) {
		return statusCodeError(resp.StatusCode)
	}

	if !strings.Contains(resp.Header.Get("Accept-Ranges"), "bytes") {
		return errors.New(r.URL.String() + " does not support byte-ranged requests.")
	}

	validator, err := validatorFromResponse(resp)
	if err != nil {
		return errors.New(r.URL.String() + " did not offer a strong-enough validator for subsequent requests")
	}

	r.blockSize = bs
	r.validator = validator
	r.length = resp.ContentLength
	return nil
}

// Length returns the length, in bytes, of the ranged-over file.
func (r *HTTPRanger) Length() int64 {
	return r.length
}

func makeByteRangeHeader(ranges []BlockByteRange) string {
	if len(ranges) > 0 {
		rs := make([]string, len(ranges))
		for i, rng := range ranges {
			rs[i] = fmt.Sprintf("%d-%d", rng.Start, rng.End)
		}
		return "bytes=" + strings.Join(rs, ",")
	}
	return ""
}

// FetchBlocks requests blocks from the HTTP server.
func (r *HTTPRanger) FetchBlocks(ranges []BlockByteRange) ([]Block, error) {
	blox := make([]Block, len(ranges))
	if len(ranges) > 0 {
		req, err := http.NewRequest("GET", r.URL.String(), nil)
		if err != nil {
			return nil, err
		}

		req.Header.Set("Range", makeByteRangeHeader(ranges))
		req.Header.Set("If-Range", r.validator)

		resp, err := r.Client.Do(req)
		if err != nil {
			return nil, err
		}

		switch resp.StatusCode {
		case http.StatusPreconditionFailed:
			return nil, ErrResourceChanged
		case http.StatusNotFound:
			return nil, ErrResourceNotFound
		case http.StatusOK:
			newValidator, err := validatorFromResponse(resp)
			if err != nil || newValidator != r.validator {
				return nil, ErrResourceChanged
			}
		default:
			if !statusIsAcceptable(resp.StatusCode) {
				return nil, statusCodeError(resp.StatusCode)
			}
		}

		defer resp.Body.Close()

		typ, params, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
		if err != nil {
			return nil, err
		}

		if typ == "multipart/byteranges" {
			multipart := multipart.NewReader(resp.Body, params["boundary"])
			i := 0
			for {
				if part, err := multipart.NextPart(); err == nil {
					rng := ranges[i]
					bn := rng.Number
					blocklen := (rng.End - rng.Start) + 1
					blox[i] = Block{Number: bn, Data: make([]byte, blocklen)}
					io.ReadFull(part, blox[i].Data)
					i++
				} else {
					if err == io.EOF {
						break
					}
					return nil, err
				}
			}
		} else {
			bn := 0
			if resp.StatusCode == http.StatusPartialContent {
				// If we've received 206 Partial Content but no multipart parts,
				// we received a contiguous section starting at the first requested block.
				bn = ranges[0].Number
			}
			body := make([]byte, resp.ContentLength)
			io.ReadFull(resp.Body, body)
			blox = blox[0:0]
			remaining := resp.ContentLength
			ncopied := int64(0)
			for remaining > 0 {
				bs := int64(r.blockSize)
				if bs > remaining {
					bs = remaining
				}

				blk := Block{bn, make([]byte, bs)}
				bodySlice := body[ncopied : ncopied+bs]
				copy(blk.Data, bodySlice)
				blox = append(blox, blk)

				bn++
				ncopied += bs
				remaining -= bs
			}
		}
	}
	return blox, nil
}
<commit_msg>Make sure the response body is closed even if we bail early<commit_after>package ranger

import (
	"errors"
	"fmt"
	"io"
	"mime"
	"mime/multipart"
	"net/http"
	"net/url"
	"strings"
)

// HTTPClient is an interface describing the methods required from net/http.Client
type HTTPClient interface {
	Do(*http.Request) (*http.Response, error)
	Get(string) (*http.Response, error)
	Head(string) (*http.Response, error)
}

// HTTPRanger is a RangeFetcher that uses the HTTP Range: header to fetch blocks.
//
// HTTPRanger first makes a HEAD request and then between 0 and Length()/BlockSize GET requests, attempting
// whenever possible to optimize for a lower number of requests.
//
// No network requests are made until the first I/O-related function call.
type HTTPRanger struct {
	URL    *url.URL
	Client HTTPClient

	validator string
	length    int64
	blockSize int
}

func statusCodeError(status int) error {
	return fmt.Errorf("unexpected response (status %d)", status)
}

func statusIsAcceptable(status int) bool {
	return status >= 200 && status < 300
}

func validatorFromResponse(resp *http.Response) (string, error) {
	etag := resp.Header.Get("ETag")
	if etag != "" && etag[0] == '"' {
		return etag, nil
	}

	modtime := resp.Header.Get("Last-Modified")
	if modtime != "" {
		return modtime, nil
	}

	return "", errors.New("no applicable validator in response")
}

// Initialize implements the Initialize function from the RangeFetcher interface.
// It performs a HEAD request to retrieve the required information from the server.
func (r *HTTPRanger) Initialize(bs int) error {
	if r.Client == nil {
		r.Client = &http.Client{}
	}

	resp, err := r.Client.Head(r.URL.String())
	if err != nil {
		return err
	}

	if !statusIsAcceptable(resp.StatusCode) {
		return statusCodeError(resp.StatusCode)
	}

	if !strings.Contains(resp.Header.Get("Accept-Ranges"), "bytes") {
		return errors.New(r.URL.String() + " does not support byte-ranged requests.")
	}

	validator, err := validatorFromResponse(resp)
	if err != nil {
		return errors.New(r.URL.String() + " did not offer a strong-enough validator for subsequent requests")
	}

	r.blockSize = bs
	r.validator = validator
	r.length = resp.ContentLength
	return nil
}

// Length returns the length, in bytes, of the ranged-over file.
func (r *HTTPRanger) Length() int64 {
	return r.length
}

func makeByteRangeHeader(ranges []BlockByteRange) string {
	if len(ranges) > 0 {
		rs := make([]string, len(ranges))
		for i, rng := range ranges {
			rs[i] = fmt.Sprintf("%d-%d", rng.Start, rng.End)
		}
		return "bytes=" + strings.Join(rs, ",")
	}
	return ""
}

// FetchBlocks requests blocks from the HTTP server.
func (r *HTTPRanger) FetchBlocks(ranges []BlockByteRange) ([]Block, error) {
	blox := make([]Block, len(ranges))
	if len(ranges) > 0 {
		req, err := http.NewRequest("GET", r.URL.String(), nil)
		if err != nil {
			return nil, err
		}

		req.Header.Set("Range", makeByteRangeHeader(ranges))
		req.Header.Set("If-Range", r.validator)

		resp, err := r.Client.Do(req)
		if err != nil {
			return nil, err
		}

		defer resp.Body.Close()

		switch resp.StatusCode {
		case http.StatusPreconditionFailed:
			return nil, ErrResourceChanged
		case http.StatusNotFound:
			return nil, ErrResourceNotFound
		case http.StatusOK:
			newValidator, err := validatorFromResponse(resp)
			if err != nil || newValidator != r.validator {
				return nil, ErrResourceChanged
			}
		default:
			if !statusIsAcceptable(resp.StatusCode) {
				return nil, statusCodeError(resp.StatusCode)
			}
		}

		typ, params, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
		if err != nil {
			return nil, err
		}

		if typ == "multipart/byteranges" {
			multipart := multipart.NewReader(resp.Body, params["boundary"])
			i := 0
			for {
				if part, err := multipart.NextPart(); err == nil {
					rng := ranges[i]
					bn := rng.Number
					blocklen := (rng.End - rng.Start) + 1
					blox[i] = Block{Number: bn, Data: make([]byte, blocklen)}
					io.ReadFull(part, blox[i].Data)
					i++
				} else {
					if err == io.EOF {
						break
					}
					return nil, err
				}
			}
		} else {
			bn := 0
			if resp.StatusCode == http.StatusPartialContent {
				// If we've received 206 Partial Content but no multipart parts,
				// we received a contiguous section starting at the first requested block.
				bn = ranges[0].Number
			}
			body := make([]byte, resp.ContentLength)
			io.ReadFull(resp.Body, body)
			blox = blox[0:0]
			remaining := resp.ContentLength
			ncopied := int64(0)
			for remaining > 0 {
				bs := int64(r.blockSize)
				if bs > remaining {
					bs = remaining
				}

				blk := Block{bn, make([]byte, bs)}
				bodySlice := body[ncopied : ncopied+bs]
				copy(blk.Data, bodySlice)
				blox = append(blox, blk)

				bn++
				ncopied += bs
				remaining -= bs
			}
		}
	}
	return blox, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"strings"
	"text/template"
	"time"
)

// Handler flywheel handler
type Handler struct {
	flywheel *Flywheel
	tmpl     *template.Template
}

// sendPing - sends a request to the flywheel to retrieve/change the state
func (handler *Handler) sendPing(op string) Pong {
	var err error

	replyTo := make(chan Pong, 1)
	sreq := Ping{replyTo: replyTo}
	switch op {
	case "start":
		sreq.requestStart = true
	case "stop":
		sreq.requestStop = true
	case "status":
		sreq.noop = true
	}
	if strings.HasPrefix(op, "stop_in:") {
		suffix := op[8:]
		dur, e := time.ParseDuration(suffix)
		if e != nil {
			err = e
		}
		sreq.setTimeout = dur
	}

	handler.flywheel.pings <- sreq
	status := <-replyTo
	if err != nil && status.Err == nil {
		status.Err = err
	}
	return status
}

func (handler *Handler) proxy(w http.ResponseWriter, r *http.Request) {
	client := &http.Client{}
	r.URL.Query().Del("flywheel")

	endpoint := handler.flywheel.ProxyEndpoint(r.Host)
	if endpoint == "" {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("Invalid flywheel endpoint config"))
		log.Fatal("Invalid endpoint URL")
	}

	r.URL.Scheme = "http"

	r.URL.Host = endpoint
	r.RequestURI = ""
	resp, err := client.Do(r)
	if err != nil {
		log.Print(err)
		w.WriteHeader(http.StatusServiceUnavailable)
		return
	}

	for key, value := range resp.Header {
		w.Header()[key] = value
	}
	w.WriteHeader(resp.StatusCode)

	_, err = io.Copy(w, resp.Body)
	if err != nil {
		log.Print(err)
	}
}

func (handler *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	log.Printf("[%s] %s %s", r.RemoteAddr, r.Method, r.RequestURI)

	query := r.URL.Query()
	param := query.Get("flywheel")

	if param == "config" {
		buf, err := json.MarshalIndent(handler.flywheel.config, "", "    ") // Might be unsafe, but this should be read only.
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprint(w, err)
		} else {
			w.Header().Set("Content-Type", "application/json")
			w.Write(buf)
		}
		return
	}

	pong := handler.sendPing(param)

	if param == "start" {
		query.Del("flywheel")
		r.URL.RawQuery = query.Encode()
		w.Header().Set("Location", r.URL.String())
		w.WriteHeader(http.StatusTemporaryRedirect)
		return
	}

	accept := query.Get("Accept")
	var acceptHTML bool
	if accept != "" {
		htmlIndex := strings.Index(accept, "text/html")
		jsonIndex := strings.Index(accept, "application/json")
		if htmlIndex != -1 {
			acceptHTML = jsonIndex == -1 || htmlIndex < jsonIndex
		}
	}

	if param != "" {
		buf, err := json.MarshalIndent(pong, "", "    ")
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprint(w, err)
			return
		}

		if param != "status" {
			query.Del("flywheel")
			r.URL.RawQuery = query.Encode()
			w.Header().Set("Content-Type", "application/json")
			if acceptHTML {
				w.Header().Set("Location", r.URL.String())
				w.WriteHeader(http.StatusTemporaryRedirect)
			}
			w.Write(buf)
		} else {
			w.Header().Set("Content-Type", "application/json")
			w.Write(buf)
		}
		return
	}

	if pong.Err != nil {
		body := fmt.Sprintf(HTMLERROR, pong.Err)
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(body))
		return
	}

	switch pong.Status {
	case STOPPED:
		query.Set("flywheel", "start")
		r.URL.RawQuery = query.Encode()
		body := fmt.Sprintf(HTMLSTOPPED, r.URL)
		w.WriteHeader(http.StatusServiceUnavailable)
		w.Write([]byte(body))
	case STARTING:
		w.WriteHeader(http.StatusServiceUnavailable)
		w.Write([]byte(HTMLSTARTING))
	case STARTED:
		handler.proxy(w, r)
	case STOPPING:
		w.WriteHeader(http.StatusServiceUnavailable)
		w.Write([]byte(HTMLSTOPPING))
	case UNHEALTHY:
		w.WriteHeader(http.StatusServiceUnavailable)
		w.Write([]byte(HTMLUNHEALTHY))
	}
}
<commit_msg>ProxyEndpoint can't never return "", so this block is unnecessary<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"strings"
	"text/template"
	"time"
)

// Handler flywheel handler
type Handler struct {
	flywheel *Flywheel
	tmpl     *template.Template
}

// sendPing - sends a request to the flywheel to retrieve/change the state
func (handler *Handler) sendPing(op string) Pong {
	var err error

	replyTo := make(chan Pong, 1)
	sreq := Ping{replyTo: replyTo}
	switch op {
	case "start":
		sreq.requestStart = true
	case "stop":
		sreq.requestStop = true
	case "status":
		sreq.noop = true
	}
	if strings.HasPrefix(op, "stop_in:") {
		suffix := op[8:]
		dur, e := time.ParseDuration(suffix)
		if e != nil {
			err = e
		}
		sreq.setTimeout = dur
	}

	handler.flywheel.pings <- sreq
	status := <-replyTo
	if err != nil && status.Err == nil {
		status.Err = err
	}
	return status
}

// TODO - refactor this function to use context
func (handler *Handler) proxy(w http.ResponseWriter, r *http.Request) {
	client := &http.Client{}

	r.URL.Host = handler.flywheel.ProxyEndpoint(r.Host)
	r.URL.Scheme = "http"
	r.RequestURI = ""
	r.URL.Query().Del("flywheel")

	resp, err := client.Do(r)
	if err != nil {
		log.Print(err)
		w.WriteHeader(http.StatusServiceUnavailable)
		return
	}

	for key, value := range resp.Header {
		w.Header()[key] = value
	}
	w.WriteHeader(resp.StatusCode)

	_, err = io.Copy(w, resp.Body)
	if err != nil {
		log.Print(err)
	}
}

func (handler *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	log.Printf("[%s] %s %s", r.RemoteAddr, r.Method, r.RequestURI)

	query := r.URL.Query()
	param := query.Get("flywheel")

	if param == "config" {
		buf, err := json.MarshalIndent(handler.flywheel.config, "", "    ") // Might be unsafe, but this should be read only.
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprint(w, err)
		} else {
			w.Header().Set("Content-Type", "application/json")
			w.Write(buf)
		}
		return
	}

	pong := handler.sendPing(param)

	if param == "start" {
		query.Del("flywheel")
		r.URL.RawQuery = query.Encode()
		w.Header().Set("Location", r.URL.String())
		w.WriteHeader(http.StatusTemporaryRedirect)
		return
	}

	accept := query.Get("Accept")
	var acceptHTML bool
	if accept != "" {
		htmlIndex := strings.Index(accept, "text/html")
		jsonIndex := strings.Index(accept, "application/json")
		if htmlIndex != -1 {
			acceptHTML = jsonIndex == -1 || htmlIndex < jsonIndex
		}
	}

	if param != "" {
		buf, err := json.MarshalIndent(pong, "", "    ")
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprint(w, err)
			return
		}

		if param != "status" {
			query.Del("flywheel")
			r.URL.RawQuery = query.Encode()
			w.Header().Set("Content-Type", "application/json")
			if acceptHTML {
				w.Header().Set("Location", r.URL.String())
				w.WriteHeader(http.StatusTemporaryRedirect)
			}
			w.Write(buf)
		} else {
			w.Header().Set("Content-Type", "application/json")
			w.Write(buf)
		}
		return
	}

	if pong.Err != nil {
		body := fmt.Sprintf(HTMLERROR, pong.Err)
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(body))
		return
	}

	switch pong.Status {
	case STOPPED:
		query.Set("flywheel", "start")
		r.URL.RawQuery = query.Encode()
		body := fmt.Sprintf(HTMLSTOPPED, r.URL)
		w.WriteHeader(http.StatusServiceUnavailable)
		w.Write([]byte(body))
	case STARTING:
		w.WriteHeader(http.StatusServiceUnavailable)
		w.Write([]byte(HTMLSTARTING))
	case STARTED:
		handler.proxy(w, r)
	case STOPPING:
		w.WriteHeader(http.StatusServiceUnavailable)
		w.Write([]byte(HTMLSTOPPING))
	case UNHEALTHY:
		w.WriteHeader(http.StatusServiceUnavailable)
		w.Write([]byte(HTMLUNHEALTHY))
	}
}
<|endoftext|>


<commit_before>package gou

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
)

// Simple Fetch Wrapper, given a url it returns bytes
func Fetch(url string) (ret []byte, err error) {
	resp, err := http.Get(url)
	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()
	if err != nil {
		Log(WARN, err.Error())
		return
	}
	ret, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	return
}

// Simple Fetch Wrapper, given a url it returns bytes and response
func FetchResp(url string) (ret []byte, err error, resp *http.Response) {
	resp, err = http.Get(url)
	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()
	if err != nil {
		Log(WARN, err.Error())
	}
	if resp == nil || resp.Body == nil {
		return
	}
	ret, err = ioutil.ReadAll(resp.Body)
	return
}

// Simple Fetch Wrapper, given a url it returns Helper, error
// Sends as type application/json, interprets whatever datatype is sent in appropriately
func JsonHelperHttp(method, urlStr string, data interface{}) (JsonHelper, error) {
	var body io.Reader
	if data != nil {
		switch val := data.(type) {
		case string:
			body = bytes.NewReader([]byte(val))
		case io.Reader:
			body = val
		case url.Values:
			body = bytes.NewReader([]byte(val.Encode()))
		default:
			by, err := json.Marshal(data)
			if err != nil {
				return nil, err
			}
			body = bytes.NewReader(by)
		}

	}
	req, err := http.NewRequest(method, urlStr, body)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Accept", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	jh, err := NewJsonHelperReader(resp.Body)
	return jh, err
}

// posts an application/json to url with body
// ie:   type = application/json
func PostJson(url, body string) (ret string, err error, resp *http.Response) {
	//Post(url string, bodyType string, body io.Reader)
	buf := bytes.NewBufferString(body)
	resp, err = http.Post(url, "application/json", buf)
	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()
	if err != nil {
		Log(WARN, err.Error())
		return "", err, resp
	}
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err, resp
	}

	return string(data), nil, resp
}

// issues http delete an application/json to url with body
func DeleteJson(url, body string) (ret string, err error, resp *http.Response) {
	//Post(url string, bodyType string, body io.Reader)
	buf := bytes.NewBufferString(body)
	Debug(buf.Len())
	req, err := http.NewRequest("DELETE", url, buf)
	if err != nil {
		Debug(err)
		return
	}

	req.Header.Add("Content-Type", "application/json")
	resp, err = http.DefaultClient.Do(req) //(url, "application/json", buf)
	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()
	if err != nil {
		Log(WARN, err.Error())
		return "", err, resp
	}
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err, resp
	}

	return string(data), nil, resp
}

// posts a www-form encoded form to url with body
func PostForm(url, body string) (ret string, err error, resp *http.Response) {
	//Post(url string, bodyType string, body io.Reader)
	buf := bytes.NewBufferString(body)
	resp, err = http.Post(url, "application/x-www-form-urlencoded", buf)
	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()
	if err != nil {
		Log(WARN, url, "  ", body, "    ", err.Error())
		return "", err, resp
	}
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err, resp
	}

	return string(data), nil, resp
}

// issues http put an application/json to url with optional body
func PutJson(url, body string) (ret string, err error, resp *http.Response) {
	buf := bytes.NewBufferString(body)
	req, err := http.NewRequest("PUT", url, buf)
	if err != nil {
		Debug(err)
		return
	}
	req.Header.Add("Content-Type", "application/json")
	resp, err = http.DefaultClient.Do(req)
	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()
	if err != nil {
		Log(WARN, err.Error())
		return "", err, resp
	}
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err, resp
	}

	return string(data), nil, resp
}
<commit_msg>update PostJson() to support any data type<commit_after>package gou

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
)

// Simple Fetch Wrapper, given a url it returns bytes
func Fetch(url string) (ret []byte, err error) {
	resp, err := http.Get(url)
	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()
	if err != nil {
		Log(WARN, err.Error())
		return
	}
	ret, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	return
}

// Simple Fetch Wrapper, given a url it returns bytes and response
func FetchResp(url string) (ret []byte, err error, resp *http.Response) {
	resp, err = http.Get(url)
	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()
	if err != nil {
		Log(WARN, err.Error())
	}
	if resp == nil || resp.Body == nil {
		return
	}
	ret, err = ioutil.ReadAll(resp.Body)
	return
}

// Simple Fetch Wrapper, given a url it returns Helper, error
// Sends as type application/json, interprets whatever datatype is sent in appropriately
func JsonHelperHttp(method, urlStr string, data interface{}) (JsonHelper, error) {
	var body io.Reader
	if data != nil {
		switch val := data.(type) {
		case string:
			body = bytes.NewReader([]byte(val))
		case io.Reader:
			body = val
		case url.Values:
			body = bytes.NewReader([]byte(val.Encode()))
		default:
			by, err := json.Marshal(data)
			if err != nil {
				return nil, err
			}
			body = bytes.NewReader(by)
		}

	}
	req, err := http.NewRequest(method, urlStr, body)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Accept", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	jh, err := NewJsonHelperReader(resp.Body)
	return jh, err
}

// posts an application/json to url with body
// ie:   type = application/json
func PostJson(postUrl string, data interface{}) (ret string, err error, resp *http.Response) {
	var buf io.Reader
	if data != nil {
		switch val := data.(type) {
		case string:
			buf = bytes.NewBufferString(val)
		case []byte:
			buf = bytes.NewReader(val)
		case json.RawMessage:
			buf = bytes.NewReader([]byte(val))
		case io.Reader:
			buf = val
		case url.Values:
			buf = bytes.NewBufferString(val.Encode())
		default:
			by, err := json.Marshal(data)
			if err != nil {
				return "", err, nil
			}
			buf = bytes.NewReader(by)
		}
	}

	resp, err = http.Post(postUrl, "application/json", buf)
	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()
	if err != nil {
		Log(WARN, err.Error())
		return "", err, resp
	}
	bodyBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err, resp
	}

	return string(bodyBytes), nil, resp
}

// issues http delete an application/json to url with body
func DeleteJson(url, body string) (ret string, err error, resp *http.Response) {
	//Post(url string, bodyType string, body io.Reader)
	buf := bytes.NewBufferString(body)
	Debug(buf.Len())
	req, err := http.NewRequest("DELETE", url, buf)
	if err != nil {
		Debug(err)
		return
	}

	req.Header.Add("Content-Type", "application/json")
	resp, err = http.DefaultClient.Do(req) //(url, "application/json", buf)
	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()
	if err != nil {
		Log(WARN, err.Error())
		return "", err, resp
	}
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err, resp
	}

	return string(data), nil, resp
}

// posts a www-form encoded form to url with body
func PostForm(url, body string) (ret string, err error, resp *http.Response) {
	//Post(url string, bodyType string, body io.Reader)
	buf := bytes.NewBufferString(body)
	resp, err = http.Post(url, "application/x-www-form-urlencoded", buf)
	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()
	if err != nil {
		Log(WARN, url, "  ", body, "    ", err.Error())
		return "", err, resp
	}
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err, resp
	}

	return string(data), nil, resp
}

// issues http put an application/json to url with optional body
func PutJson(url, body string) (ret string, err error, resp *http.Response) {
	buf := bytes.NewBufferString(body)
	req, err := http.NewRequest("PUT", url, buf)
	if err != nil {
		Debug(err)
		return
	}
	req.Header.Add("Content-Type", "application/json")
	resp, err = http.DefaultClient.Do(req)
	defer func() {
		if resp != nil && resp.Body != nil {
			resp.Body.Close()
		}
	}()
	if err != nil {
		Log(WARN, err.Error())
		return "", err, resp
	}
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err, resp
	}

	return string(data), nil, resp
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/gorilla/mux"
	"github.com/minotar/minecraft"
	"net/http"
	"strconv"
	"strings"
)

type Router struct {
	Mux *mux.Router
}

type NotFoundHandler struct{}

// Handles 404 errors
func (h NotFoundHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(404)
	fmt.Fprintf(w, "404 not found")
}

// Converts and sanitizes the string for the avatar size.
func (r *Router) GetSize(inp string) uint {
	out64, err := strconv.ParseUint(inp, 10, 0)
	out := uint(out64)
	if err != nil {
		return DefaultSize
	} else if out > MaxSize {
		return MaxSize
	} else if out < MinSize {
		return MinSize
	}
	return out

}

// Shows only the user's skin.
func (router *Router) SkinPage(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	username := vars["username"]

	skin := fetchSkin(username)

	w.Header().Add("Content-Type", "image/png")
	w.Header().Add("X-Requested", "skin")
	w.Header().Add("X-Result", "ok")

	skin.WriteSkin(w)
}

// Shows the skin and tells the browser to attempt to download it.
func (router *Router) DownloadPage(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Content-Disposition", "attachment; filename=\"skin.png\"")
	router.SkinPage(w, r)
}

// Pull the Get<resource> method from the skin. Originally this used
// reflection, but that was slow.
func (router *Router) ResolveMethod(skin *mcSkin, resource string) func(int) error {
	stats.Served(resource)

	switch resource {
	case "Avatar":
		return skin.GetHead
	case "Helm":
		return skin.GetHelm
	case "Cube":
		return skin.GetCube
	case "Bust":
		return skin.GetBust
	case "Body":
		return skin.GetBody
	case "Armor/Bust":
		return skin.GetArmorBust
	case "Armour/Bust":
		return skin.GetArmorBust
	case "Armor/Body":
		return skin.GetArmorBody
	case "Armour/Body":
		return skin.GetArmorBody
	default:
		return skin.GetHelm
	}
}

func (router *Router) getResizeMode(ext string) string {
	switch ext {
	case ".svg":
		return "None"
	default:
		return "Normal"
	}
}

func (router *Router) writeType(ext string, skin *mcSkin, w http.ResponseWriter) {
	w.Header().Add("Cache-Control", fmt.Sprintf("public, max-age=%d", TimeoutActualSkin))
	w.Header().Add("ETag", skin.Hash)
	switch ext {
	case ".svg":
		w.Header().Add("Content-Type", "image/svg+xml")
		skin.WriteSVG(w)
	default:
		w.Header().Add("Content-Type", "image/png")
		skin.WritePNG(w)
	}
}

// Binds the route and makes a handler function for the requested resource.
func (router *Router) Serve(resource string) {
	fn := func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		size := router.GetSize(vars["size"])
		skin := fetchSkin(vars["username"])
		skin.Mode = router.getResizeMode(vars["extension"])

		if r.Header.Get("If-None-Match") == skin.Skin.Hash {
			w.WriteHeader(http.StatusNotModified)
			return
		}

		err := router.ResolveMethod(skin, resource)(int(size))
		if err != nil {
			w.WriteHeader(500)
			fmt.Fprintf(w, "500 internal server error")
			return
		}
		router.writeType(vars["extension"], skin, w)
	}

	router.Mux.HandleFunc("/"+strings.ToLower(resource)+"/{username:"+minecraft.ValidUsernameRegex+"}{extension:(\\..*)?}", fn)
	router.Mux.HandleFunc("/"+strings.ToLower(resource)+"/{username:"+minecraft.ValidUsernameRegex+"}/{size:[0-9]+}{extension:(\\..*)?}", fn)
}

// Binds routes to the ServerMux.
func (router *Router) Bind() {

	router.Mux.NotFoundHandler = NotFoundHandler{}

	router.Serve("Avatar")
	router.Serve("Helm")
	router.Serve("Cube")
	router.Serve("Bust")
	router.Serve("Body")
	router.Serve("Armor/Bust")
	router.Serve("Armour/Bust")
	router.Serve("Armor/Body")
	router.Serve("Armour/Body")

	router.Mux.HandleFunc("/download/{username:"+minecraft.ValidUsernameRegex+"}{extension:(.png)?}", router.DownloadPage)
	router.Mux.HandleFunc("/skin/{username:"+minecraft.ValidUsernameRegex+"}{extension:(.png)?}", router.SkinPage)

	router.Mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "%s", MinotarVersion)
	})

	router.Mux.HandleFunc("/stats", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		w.Write(stats.ToJSON())
	})

	router.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "https://minotar.net/", 302)
	})
}

func fetchSkin(username string) *mcSkin {
	if username == "char" {
		skin, _ := minecraft.FetchSkinForChar()
		return &mcSkin{Skin: skin}
	}

	if cache.has(strings.ToLower(username)) {
		stats.HitCache()
		return &mcSkin{Processed: nil, Skin: cache.pull(strings.ToLower(username))}
	}

	skin, err := minecraft.FetchSkinFromMojang(username)
	if err != nil {
		log.Error("Failed Skin Mojang: " + username + " (" + err.Error() + ")")
		// Let's fallback to S3 and try and serve at least an old skin...
		skin, err = minecraft.FetchSkinFromS3(username)
		if err != nil {
			log.Error("Failed Skin S3: " + username + " (" + err.Error() + ")")
			// Well, looks like they don't exist after all.
			skin, _ = minecraft.FetchSkinForChar()
		}
	}

	stats.MissCache()
	cache.add(strings.ToLower(username), skin)

	return &mcSkin{Processed: nil, Skin: skin}
}
<commit_msg>Use ttl for cache control<commit_after>package main

import (
	"fmt"
	"github.com/gorilla/mux"
	"github.com/minotar/minecraft"
	"net/http"
	"strconv"
	"strings"
)

type Router struct {
	Mux *mux.Router
}

type NotFoundHandler struct{}

// Handles 404 errors
func (h NotFoundHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(404)
	fmt.Fprintf(w, "404 not found")
}

// Converts and sanitizes the string for the avatar size.
func (r *Router) GetSize(inp string) uint {
	out64, err := strconv.ParseUint(inp, 10, 0)
	out := uint(out64)
	if err != nil {
		return DefaultSize
	} else if out > MaxSize {
		return MaxSize
	} else if out < MinSize {
		return MinSize
	}
	return out

}

// Shows only the user's skin.
func (router *Router) SkinPage(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	username := vars["username"]

	skin := fetchSkin(username)

	w.Header().Add("Content-Type", "image/png")
	w.Header().Add("X-Requested", "skin")
	w.Header().Add("X-Result", "ok")

	skin.WriteSkin(w)
}

// Shows the skin and tells the browser to attempt to download it.
func (router *Router) DownloadPage(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Content-Disposition", "attachment; filename=\"skin.png\"")
	router.SkinPage(w, r)
}

// Pull the Get<resource> method from the skin. Originally this used
// reflection, but that was slow.
func (router *Router) ResolveMethod(skin *mcSkin, resource string) func(int) error {
	stats.Served(resource)

	switch resource {
	case "Avatar":
		return skin.GetHead
	case "Helm":
		return skin.GetHelm
	case "Cube":
		return skin.GetCube
	case "Bust":
		return skin.GetBust
	case "Body":
		return skin.GetBody
	case "Armor/Bust":
		return skin.GetArmorBust
	case "Armour/Bust":
		return skin.GetArmorBust
	case "Armor/Body":
		return skin.GetArmorBody
	case "Armour/Body":
		return skin.GetArmorBody
	default:
		return skin.GetHelm
	}
}

func (router *Router) getResizeMode(ext string) string {
	switch ext {
	case ".svg":
		return "None"
	default:
		return "Normal"
	}
}

func (router *Router) writeType(ext string, skin *mcSkin, w http.ResponseWriter) {
	w.Header().Add("Cache-Control", fmt.Sprintf("public, max-age=%d", config.Server.Ttl))
	w.Header().Add("ETag", skin.Hash)
	switch ext {
	case ".svg":
		w.Header().Add("Content-Type", "image/svg+xml")
		skin.WriteSVG(w)
	default:
		w.Header().Add("Content-Type", "image/png")
		skin.WritePNG(w)
	}
}

// Binds the route and makes a handler function for the requested resource.
func (router *Router) Serve(resource string) {
	fn := func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		size := router.GetSize(vars["size"])
		skin := fetchSkin(vars["username"])
		skin.Mode = router.getResizeMode(vars["extension"])

		if r.Header.Get("If-None-Match") == skin.Skin.Hash {
			w.WriteHeader(http.StatusNotModified)
			return
		}

		err := router.ResolveMethod(skin, resource)(int(size))
		if err != nil {
			w.WriteHeader(500)
			fmt.Fprintf(w, "500 internal server error")
			return
		}
		router.writeType(vars["extension"], skin, w)
	}

	router.Mux.HandleFunc("/"+strings.ToLower(resource)+"/{username:"+minecraft.ValidUsernameRegex+"}{extension:(\\..*)?}", fn)
	router.Mux.HandleFunc("/"+strings.ToLower(resource)+"/{username:"+minecraft.ValidUsernameRegex+"}/{size:[0-9]+}{extension:(\\..*)?}", fn)
}

// Binds routes to the ServerMux.
func (router *Router) Bind() {

	router.Mux.NotFoundHandler = NotFoundHandler{}

	router.Serve("Avatar")
	router.Serve("Helm")
	router.Serve("Cube")
	router.Serve("Bust")
	router.Serve("Body")
	router.Serve("Armor/Bust")
	router.Serve("Armour/Bust")
	router.Serve("Armor/Body")
	router.Serve("Armour/Body")

	router.Mux.HandleFunc("/download/{username:"+minecraft.ValidUsernameRegex+"}{extension:(.png)?}", router.DownloadPage)
	router.Mux.HandleFunc("/skin/{username:"+minecraft.ValidUsernameRegex+"}{extension:(.png)?}", router.SkinPage)

	router.Mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "%s", MinotarVersion)
	})

	router.Mux.HandleFunc("/stats", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		w.Write(stats.ToJSON())
	})

	router.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "https://minotar.net/", 302)
	})
}

func fetchSkin(username string) *mcSkin {
	if username == "char" {
		skin, _ := minecraft.FetchSkinForChar()
		return &mcSkin{Skin: skin}
	}

	if cache.has(strings.ToLower(username)) {
		stats.HitCache()
		return &mcSkin{Processed: nil, Skin: cache.pull(strings.ToLower(username))}
	}

	skin, err := minecraft.FetchSkinFromMojang(username)
	if err != nil {
		log.Error("Failed Skin Mojang: " + username + " (" + err.Error() + ")")
		// Let's fallback to S3 and try and serve at least an old skin...
		skin, err = minecraft.FetchSkinFromS3(username)
		if err != nil {
			log.Error("Failed Skin S3: " + username + " (" + err.Error() + ")")
			// Well, looks like they don't exist after all.
			skin, _ = minecraft.FetchSkinForChar()
		}
	}

	stats.MissCache()
	cache.add(strings.ToLower(username), skin)

	return &mcSkin{Processed: nil, Skin: skin}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"

	"github.com/flynn/flynn/pkg/httphelper"
	"github.com/julienschmidt/httprouter"
)

type Server struct {
	URL     string
	Backend StorageBackend
	router  *httprouter.Router
}

func NewServer(url string, backend StorageBackend) *Server {
	s := &Server{
		URL:     url,
		Backend: backend,
		router:  httprouter.New(),
	}
	s.router.POST("/clusters", s.CreateCluster)
	s.router.POST("/clusters/:cluster_id/instances", s.CreateInstance)
	s.router.GET("/clusters/:cluster_id/instances", s.GetInstances)

	return s
}

func (s *Server) CreateCluster(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
	cluster := &Cluster{
		CreatorIP:        req.RemoteAddr, // TODO: parse X-Forwarded-For
		CreatorUserAgent: req.Header.Get("User-Agent"),
	}

	if len(cluster.CreatorUserAgent) > 1000 {
		cluster.CreatorUserAgent = cluster.CreatorUserAgent[:1000]
	}

	if err := s.Backend.CreateCluster(cluster); err != nil {
		httphelper.Error(w, err)
		return
	}

	w.Header().Set("Location", fmt.Sprintf("%s/clusters/%s", s.URL, cluster.ID))
	w.WriteHeader(http.StatusCreated)
}

func (s *Server) CreateInstance(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	var data struct {
		Data *Instance `json:"data"`
	}
	if err := httphelper.DecodeJSON(req, &data); err != nil {
		httphelper.Error(w, err)
		return
	}
	inst := data.Data
	inst.ClusterID = params.ByName("cluster_id")
	inst.CreatorIP = req.RemoteAddr // TODO: parse X-Forwarded-For
	// TODO: validate with JSON schema

	status := http.StatusCreated
	if err := s.Backend.CreateInstance(inst); err == ErrExists {
		status = http.StatusConflict
	} else if err != nil {
		httphelper.Error(w, err)
		return
	}

	w.Header().Set("Location", fmt.Sprintf("%s/clusters/%s/instances/%s", s.URL, inst.ClusterID, inst.ID))
	httphelper.JSON(w, status, data)
}

func (s *Server) GetInstances(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	instances, err := s.Backend.GetClusterInstances(params.ByName("cluster_id"))
	if err != nil {
		httphelper.Error(w, err)
		return
	}
	if instances == nil {
		instances = []*Instance{}
	}
	httphelper.JSON(w, 200, struct {
		Data []*Instance `json:"data"`
	}{instances})
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	s.router.ServeHTTP(w, r)
}
<commit_msg>Parse X-Forwarded-For header<commit_after>package main

import (
	"fmt"
	"net"
	"net/http"
	"strings"

	"github.com/flynn/flynn/pkg/httphelper"
	"github.com/julienschmidt/httprouter"
)

type Server struct {
	URL     string
	Backend StorageBackend
	router  *httprouter.Router
}

func NewServer(url string, backend StorageBackend) *Server {
	s := &Server{
		URL:     url,
		Backend: backend,
		router:  httprouter.New(),
	}
	s.router.POST("/clusters", s.CreateCluster)
	s.router.POST("/clusters/:cluster_id/instances", s.CreateInstance)
	s.router.GET("/clusters/:cluster_id/instances", s.GetInstances)

	return s
}

func (s *Server) CreateCluster(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
	cluster := &Cluster{
		CreatorIP:        sourceIP(req),
		CreatorUserAgent: req.Header.Get("User-Agent"),
	}

	if len(cluster.CreatorUserAgent) > 1000 {
		cluster.CreatorUserAgent = cluster.CreatorUserAgent[:1000]
	}

	if err := s.Backend.CreateCluster(cluster); err != nil {
		httphelper.Error(w, err)
		return
	}

	w.Header().Set("Location", fmt.Sprintf("%s/clusters/%s", s.URL, cluster.ID))
	w.WriteHeader(http.StatusCreated)
}

func (s *Server) CreateInstance(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	var data struct {
		Data *Instance `json:"data"`
	}
	if err := httphelper.DecodeJSON(req, &data); err != nil {
		httphelper.Error(w, err)
		return
	}
	inst := data.Data
	inst.ClusterID = params.ByName("cluster_id")
	inst.CreatorIP = sourceIP(req)
	// TODO: validate with JSON schema

	status := http.StatusCreated
	if err := s.Backend.CreateInstance(inst); err == ErrExists {
		status = http.StatusConflict
	} else if err != nil {
		httphelper.Error(w, err)
		return
	}

	w.Header().Set("Location", fmt.Sprintf("%s/clusters/%s/instances/%s", s.URL, inst.ClusterID, inst.ID))
	httphelper.JSON(w, status, data)
}

func (s *Server) GetInstances(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
	instances, err := s.Backend.GetClusterInstances(params.ByName("cluster_id"))
	if err != nil {
		httphelper.Error(w, err)
		return
	}
	if instances == nil {
		instances = []*Instance{}
	}
	httphelper.JSON(w, 200, struct {
		Data []*Instance `json:"data"`
	}{instances})
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	s.router.ServeHTTP(w, r)
}

func sourceIP(req *http.Request) string {
	if xff := req.Header.Get("X-Forwarded-For"); xff != "" {
		ips := strings.Split(xff, ",")
		return strings.TrimSpace(ips[len(ips)-1])
	}
	ip, _, _ := net.SplitHostPort(req.RemoteAddr)
	return ip
}
<|endoftext|>


<commit_before>package sms // import "github.com/mndrix/sms-over-xmpp"
import (
	"encoding/xml"
	"fmt"
	"log"
	"net/http"

	xco "github.com/mndrix/go-xco"
)

// httpAgent is the piece which listens for incoming HTTP requests and
// converts them into values which the rest of the system can
// understand.
type httpAgent struct {
	// where to listen for incoming HTTP requests
	host string
	port int

	// credentials for HTTP auth
	user     string
	password string

	// this field is only temporary. remove after refactoring
	sc *Component
}

// run creates a goroutine for receiving HTTP requests.  It returns a
// channel for monitoring the goroutine's health.  If that channel
// closes, the HTTP goroutine has died.
func (h *httpAgent) run() <-chan struct{} {
	addr := fmt.Sprintf("%s:%d", h.host, h.port)
	healthCh := make(chan struct{})
	go func() {
		defer func() { close(healthCh) }()
		err := http.ListenAndServe(addr, h)
		log.Printf("HTTP server error: %s", err)
	}()
	return healthCh
}

func (h *httpAgent) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	sc := h.sc
	msgSid := r.FormValue("MessageSid")
	log.Printf("%s %s (%s)", r.Method, r.URL.Path, msgSid)

	// verify HTTP authentication
	if !h.isHttpAuthenticated(r) {
		w.Header().Set("WWW-Authenticate", "Basic realm=\"sms-over-xmpp\"")
		w.WriteHeader(http.StatusUnauthorized)
		fmt.Fprintln(w, "Not authorized")
		return
	}

	// which SMS provider is applicable?
	provider, err := sc.config.SmsProvider()
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		msg := "ignored during provider selection"
		log.Println(msg)
		return
	default:
		msg := fmt.Sprintf("ERROR: choosing an SMS provider: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, msg)
		log.Println(msg)
		return
	}

	// is this an SMS Status update?
	if p, ok := provider.(CanSmsStatus); ok {
		if smsId, status, ok := p.SmsStatus(r); ok {
			if status == "delivered" {
				sc.receiptForMutex.Lock()
				defer func() { sc.receiptForMutex.Unlock() }()
				if receipt, ok := sc.receiptFor[smsId]; ok {
					err := sc.xmppSend(receipt)
					if err != nil {
						log.Printf("ERROR sending SMS delivery receipt: %s", err)
						return
					}
					log.Printf("Sent SMS delivery receipt")
					delete(sc.receiptFor, smsId)
				}
			}
			return
		}
	}

	fromPhone, toPhone, body, err := provider.ReceiveSms(r)
	if err != nil {
		log.Printf("ERROR receiving SMS: %s", err)
		return
	}

	// convert author's phone number into XMPP address
	from, err := sc.config.PhoneToAddress(fromPhone)
	switch err {
	case nil:
		// all is well. proceed
	case ErrIgnoreMessage:
		msg := "ignored based on From address"
		log.Println(msg)
		return
	default:
		msg := fmt.Sprintf("ERROR: From address %s: %s", fromPhone, err)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, msg)
		log.Println(msg)
		return
	}

	// convert recipient's phone number into XMPP address
	to, err := sc.config.PhoneToAddress(toPhone)
	switch err {
	case nil:
		// all is well. proceed
	case ErrIgnoreMessage:
		msg := "ignored based on To address"
		log.Println(msg)
		return
	default:
		msg := fmt.Sprintf("ERROR: To address %s: %s", toPhone, err)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, msg)
		log.Println(msg)
		return
	}

	// deliver message over XMPP
	msg := &xco.Message{
		XMLName: xml.Name{
			Local: "message",
			Space: "jabber:component:accept",
		},

		Header: xco.Header{
			From: from,
			To:   to,
			ID:   NewId(),
		},
		Type: "chat",
		Body: body,
	}
	err = sc.xmppSend(msg)
	if err != nil {
		log.Printf("ERROR: can't send message: %s", err)
	}
}

func (h *httpAgent) isHttpAuthenticated(r *http.Request) bool {
	wantUser := h.user
	wantPass := h.password
	if wantUser == "" && wantPass == "" {
		return true
	}

	// now we know that HTTP authentication is mandatory
	gotUser, gotPass, ok := r.BasicAuth()
	if !ok {
		return false
	}

	return gotUser == wantUser && gotPass == wantPass
}
<commit_msg>Refactor a distinction for SMS -> XMPP translation<commit_after>package sms // import "github.com/mndrix/sms-over-xmpp"
import (
	"encoding/xml"
	"fmt"
	"log"
	"net/http"

	xco "github.com/mndrix/go-xco"
	"github.com/pkg/errors"
)

// httpAgent is the piece which listens for incoming HTTP requests and
// converts them into values which the rest of the system can
// understand.
type httpAgent struct {
	// where to listen for incoming HTTP requests
	host string
	port int

	// credentials for HTTP auth
	user     string
	password string

	// this field is only temporary. remove after refactoring
	sc *Component
}

// run creates a goroutine for receiving HTTP requests.  It returns a
// channel for monitoring the goroutine's health.  If that channel
// closes, the HTTP goroutine has died.
func (h *httpAgent) run() <-chan struct{} {
	addr := fmt.Sprintf("%s:%d", h.host, h.port)
	healthCh := make(chan struct{})
	go func() {
		defer func() { close(healthCh) }()
		err := http.ListenAndServe(addr, h)
		log.Printf("HTTP server error: %s", err)
	}()
	return healthCh
}

func (h *httpAgent) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	sc := h.sc
	msgSid := r.FormValue("MessageSid")
	log.Printf("%s %s (%s)", r.Method, r.URL.Path, msgSid)

	// verify HTTP authentication
	if !h.isHttpAuthenticated(r) {
		w.Header().Set("WWW-Authenticate", "Basic realm=\"sms-over-xmpp\"")
		w.WriteHeader(http.StatusUnauthorized)
		fmt.Fprintln(w, "Not authorized")
		return
	}

	// which SMS provider is applicable?
	provider, err := sc.config.SmsProvider()
	switch err {
	case nil:
		// all is well. we'll continue below
	case ErrIgnoreMessage:
		msg := "ignored during provider selection"
		log.Println(msg)
		return
	default:
		msg := fmt.Sprintf("ERROR: choosing an SMS provider: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, msg)
		log.Println(msg)
		return
	}

	// is this an SMS Status update?
	if p, ok := provider.(CanSmsStatus); ok {
		if smsId, status, ok := p.SmsStatus(r); ok {
			if status == "delivered" {
				sc.receiptForMutex.Lock()
				defer func() { sc.receiptForMutex.Unlock() }()
				if receipt, ok := sc.receiptFor[smsId]; ok {
					err := sc.xmppSend(receipt)
					if err != nil {
						log.Printf("ERROR sending SMS delivery receipt: %s", err)
						return
					}
					log.Printf("Sent SMS delivery receipt")
					delete(sc.receiptFor, smsId)
				}
			}
			return
		}
	}

	fromPhone, toPhone, body, err := provider.ReceiveSms(r)
	if err != nil {
		log.Printf("ERROR receiving SMS: %s", err)
		return
	}

	err = sc.sms2xmpp(fromPhone, toPhone, body)
	if err != nil {
		msg := fmt.Sprintf("ERROR: %s", err)
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, msg)
		log.Println(msg)
		return
	}
}

func (sc *Component) sms2xmpp(fromPhone, toPhone, body string) error {

	// convert author's phone number into XMPP address
	from, err := sc.config.PhoneToAddress(fromPhone)
	switch err {
	case nil:
		// all is well. proceed
	case ErrIgnoreMessage:
		msg := "ignored based on From address"
		log.Println(msg)
		return nil
	default:
		return errors.Wrap(err, "From address "+fromPhone)
	}

	// convert recipient's phone number into XMPP address
	to, err := sc.config.PhoneToAddress(toPhone)
	switch err {
	case nil:
		// all is well. proceed
	case ErrIgnoreMessage:
		msg := "ignored based on To address"
		log.Println(msg)
		return nil
	default:
		return errors.Wrap(err, "To address "+toPhone)
	}

	// deliver message over XMPP
	msg := &xco.Message{
		XMLName: xml.Name{
			Local: "message",
			Space: "jabber:component:accept",
		},

		Header: xco.Header{
			From: from,
			To:   to,
			ID:   NewId(),
		},
		Type: "chat",
		Body: body,
	}
	err = sc.xmppSend(msg)
	return errors.Wrap(err, "can't send message")
}

func (h *httpAgent) isHttpAuthenticated(r *http.Request) bool {
	wantUser := h.user
	wantPass := h.password
	if wantUser == "" && wantPass == "" {
		return true
	}

	// now we know that HTTP authentication is mandatory
	gotUser, gotPass, ok := r.BasicAuth()
	if !ok {
		return false
	}

	return gotUser == wantUser && gotPass == wantPass
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"text/template"
	"time"

	"github.com/garyburd/redigo/redis"
	"github.com/gorilla/mux"
)

type UserAgentInfo struct {
	AgentType         string `json:"agent_type"`
	AgentName         string `json:"agent_name"`
	AgentVersion      string `json:"agent_version"`
	OSType            string `json:"os_type"`
	OSName            string `json:"os_name"`
	OSVersionName     string `json:"os_versionName"`
	OSVersionNumber   string `json:"os_versionNumber"`
	LinuxDistribution string `json:"linux_distribution"`
}

type HttpRequest struct {
	Request
	Url           string        `json:"url"`
	FullUrl       string        `json:"full_url"`
	Method        string        `json:"method"`
	Headers       http.Header   `json:"headers"`
	Body          string        `json:"body"`
	Host          string        `json:"host"`
	PostForm      url.Values    `json:"post_form"`
	Form          url.Values    `json:"form"`
	JSON          interface{}   `json:"json"`
	BinId         string        `json:"bin_id"`
	UserAgentInfo UserAgentInfo `json:"user_agent_info"`
}

func (request *HttpRequest) ISO8601Time() string {
	return request.Time.Format(time.RFC3339)
}

func UrlEncoded(str string) (string, error) {
	u, err := url.Parse(str)
	if err != nil {
		return "", err
	}
	return u.String(), nil
}

func GetUserAgentInfo(r *http.Request) (UserAgentInfo, error) {
	var userAgentInfo UserAgentInfo
	userAgent := r.Header["User-Agent"]
	if userAgent != nil {
		userAgent, err := UrlEncoded(userAgent[0])
		if err != nil {
			return userAgentInfo, err
		}

		lookupUrl := fmt.Sprintf("http://www.useragentstring.com/?uas=%s&getJSON=all", userAgent)
		res, err := http.Get(lookupUrl)
		if err != nil {
			return userAgentInfo, err
		}

		defer res.Body.Close()

		decoder := json.NewDecoder(res.Body)
		err = decoder.Decode(&userAgentInfo)
		if err != nil {
			return userAgentInfo, err
		}
	}
	return userAgentInfo, nil
}

func startLoggingHttpServer(port int, staticRoot string, writers []HttpRequestWriter) {
	fmt.Printf("Starting HTTP logging server on port %d\n", port)

	handlers := []HttpRequestHandler{
		RedirectHttpRequestHandler{},
		StaticFileHttpRequestHandler{},
		DynamicFileHttpRequestHandler{},
	}

	router := mux.NewRouter().StrictSlash(true)
	router.PathPrefix("/files").Handler(http.FileServer(http.Dir(staticRoot)))
	router.HandleFunc("/", LogHandler(handlers, writers))
	router.HandleFunc("/{binId}", LogHandler(handlers, writers))
	router.HandleFunc("/{binId}/{param:.*}", LogHandler(handlers, writers))
	go http.ListenAndServe(fmt.Sprintf(":%d", port), router)
}

func startAdminHttpServer(port int, staticRoot string, redisClient redis.Conn) {
	fmt.Printf("Starting HTTP admin server on port %d\n", port)

	router := mux.NewRouter().StrictSlash(true)
	router.HandleFunc("/api/bins", ApiBinIndexHandler(redisClient))
	router.HandleFunc("/api/bins/{binId}", ApiBinHandler(redisClient))
	router.HandleFunc("/{binId}", BinHandler(redisClient))
	router.HandleFunc("/", HomeHandler)
	router.PathPrefix("/").Handler(http.FileServer(http.Dir(staticRoot)))
	go http.ListenAndServe(fmt.Sprintf(":%d", port), router)
}

func ParseHttpRequest(r *http.Request, binId string) HttpRequest {
	// Read the content
	var bodyBytes []byte
	if r.Body != nil {
		bodyBytes, _ = ioutil.ReadAll(r.Body)
	}
	// Restore the io.ReadCloser to its original state
	r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))

	// Use the content
	body := string(bodyBytes)

	if err := r.ParseForm(); err != nil {
		panic(err)
	}

	var json_content interface{}
	if err := json.NewDecoder(r.Body).Decode(&json_content); err != nil {
		// ignore json parsing errors
	}

	fullUrl := r.URL
	fullUrl.Host = r.Host
	fullUrl.Scheme = "http" // TODO find a solution
	fullUrl.RawQuery = r.URL.Query().Encode()

	userAgentInfo, _ := GetUserAgentInfo(r)

	return HttpRequest{
		Request{
			Time:       time.Now(),
			RemoteAddr: r.RemoteAddr,
		},
		r.URL.Path,
		fullUrl.String(),
		r.Method,
		r.Header,
		string(body),
		r.Host,
		r.PostForm,
		r.Form,
		json_content,
		binId,
		userAgentInfo,
	}
}

func json_response(w http.ResponseWriter, body interface{}) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(body); err != nil {
		panic(err)
	}
}

func ApiBinHandler(redisClient redis.Conn) func(http.ResponseWriter, *http.Request) {

	return func(w http.ResponseWriter, r *http.Request) {
		bin := mux.Vars(r)["binId"]
		requests := ListRequestsFromBin(redisClient, bin)
		json_response(w, requests)
	}
}

func ApiBinIndexHandler(redisClient redis.Conn) func(http.ResponseWriter, *http.Request) {

	return func(w http.ResponseWriter, r *http.Request) {
		bins := ListBins(redisClient)
		json_response(w, bins)
	}
}

func HomeHandler(w http.ResponseWriter, r *http.Request) {
	params := struct {
		Title string
	}{Title: "Welcome to RequestBin"}

	getTemplate(w, "home").ExecuteTemplate(w, "base", params)
}

func BinHandler(redisClient redis.Conn) func(http.ResponseWriter, *http.Request) {

	return func(w http.ResponseWriter, r *http.Request) {
		binId := mux.Vars(r)["binId"]
		requests := ListRequestsFromBin(redisClient, binId)

		params := struct {
			Requests []HttpRequest
			Title    string
		}{
			Requests: requests,
			Title:    "Bin #" + binId,
		}

		err := getTemplate(w, "bin").ExecuteTemplate(w, "base", params)
		if err != nil {
			panic(err)
		}
	}
}

func RenderDocumentTemplate(writer io.Writer, source string, trackerUrl string) {
	t := template.Must(template.New("template").ParseFiles(source))
	params := struct{ Url string }{Url: trackerUrl}
	if err := t.ExecuteTemplate(writer, "document", params); err != nil {
		fmt.Println(err)
	}
}

func getTemplate(w http.ResponseWriter, tmpl string) *template.Template {
	funcMap := template.FuncMap{
		"lookup_addr": func(addr string) []string {
			names, err := net.LookupAddr(addr)
			if err != nil {
				return []string{addr}
			}
			return names
		},
	}
	templates := template.New("template").Funcs(funcMap)
	templates_folder := os.Getenv("ROOT") + "/templates/"
	_, err := templates.ParseFiles(templates_folder+"base.html", templates_folder+tmpl+".html")
	if err != nil {
		fmt.Println(err)
	}

	return template.Must(templates, err)
}

func LogHandler(handlers []HttpRequestHandler, writers []HttpRequestWriter) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		binId, ok := mux.Vars(r)["binId"]
		if !ok {
			binId = "_root"
		}
		request := ParseHttpRequest(r, binId)
		for _, writer := range writers {
			err := writer.WriteHttpRequest(request)
			if err != nil {
				fmt.Println(err)
			}
		}

		for _, handler := range handlers {
			if handler.Handle(w, r, &request) {
				return
			}
		}

		json_response(w, request)

	}
}
<commit_msg>Reorder static/dynamic to fallback on static files<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"text/template"
	"time"

	"github.com/garyburd/redigo/redis"
	"github.com/gorilla/mux"
)

type UserAgentInfo struct {
	AgentType         string `json:"agent_type"`
	AgentName         string `json:"agent_name"`
	AgentVersion      string `json:"agent_version"`
	OSType            string `json:"os_type"`
	OSName            string `json:"os_name"`
	OSVersionName     string `json:"os_versionName"`
	OSVersionNumber   string `json:"os_versionNumber"`
	LinuxDistribution string `json:"linux_distribution"`
}

type HttpRequest struct {
	Request
	Url           string        `json:"url"`
	FullUrl       string        `json:"full_url"`
	Method        string        `json:"method"`
	Headers       http.Header   `json:"headers"`
	Body          string        `json:"body"`
	Host          string        `json:"host"`
	PostForm      url.Values    `json:"post_form"`
	Form          url.Values    `json:"form"`
	JSON          interface{}   `json:"json"`
	BinId         string        `json:"bin_id"`
	UserAgentInfo UserAgentInfo `json:"user_agent_info"`
}

func (request *HttpRequest) ISO8601Time() string {
	return request.Time.Format(time.RFC3339)
}

func UrlEncoded(str string) (string, error) {
	u, err := url.Parse(str)
	if err != nil {
		return "", err
	}
	return u.String(), nil
}

func GetUserAgentInfo(r *http.Request) (UserAgentInfo, error) {
	var userAgentInfo UserAgentInfo
	userAgent := r.Header["User-Agent"]
	if userAgent != nil {
		userAgent, err := UrlEncoded(userAgent[0])
		if err != nil {
			return userAgentInfo, err
		}

		lookupUrl := fmt.Sprintf("http://www.useragentstring.com/?uas=%s&getJSON=all", userAgent)
		res, err := http.Get(lookupUrl)
		if err != nil {
			return userAgentInfo, err
		}

		defer res.Body.Close()

		decoder := json.NewDecoder(res.Body)
		err = decoder.Decode(&userAgentInfo)
		if err != nil {
			return userAgentInfo, err
		}
	}
	return userAgentInfo, nil
}

func startLoggingHttpServer(port int, staticRoot string, writers []HttpRequestWriter) {
	fmt.Printf("Starting HTTP logging server on port %d\n", port)

	handlers := []HttpRequestHandler{
		RedirectHttpRequestHandler{},
		DynamicFileHttpRequestHandler{},
		StaticFileHttpRequestHandler{},
	}

	router := mux.NewRouter().StrictSlash(true)
	router.PathPrefix("/files").Handler(http.FileServer(http.Dir(staticRoot)))
	router.HandleFunc("/", LogHandler(handlers, writers))
	router.HandleFunc("/{binId}", LogHandler(handlers, writers))
	router.HandleFunc("/{binId}/{param:.*}", LogHandler(handlers, writers))
	go http.ListenAndServe(fmt.Sprintf(":%d", port), router)
}

func startAdminHttpServer(port int, staticRoot string, redisClient redis.Conn) {
	fmt.Printf("Starting HTTP admin server on port %d\n", port)

	router := mux.NewRouter().StrictSlash(true)
	router.HandleFunc("/api/bins", ApiBinIndexHandler(redisClient))
	router.HandleFunc("/api/bins/{binId}", ApiBinHandler(redisClient))
	router.HandleFunc("/{binId}", BinHandler(redisClient))
	router.HandleFunc("/", HomeHandler)
	router.PathPrefix("/").Handler(http.FileServer(http.Dir(staticRoot)))
	go http.ListenAndServe(fmt.Sprintf(":%d", port), router)
}

func ParseHttpRequest(r *http.Request, binId string) HttpRequest {
	// Read the content
	var bodyBytes []byte
	if r.Body != nil {
		bodyBytes, _ = ioutil.ReadAll(r.Body)
	}
	// Restore the io.ReadCloser to its original state
	r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))

	// Use the content
	body := string(bodyBytes)

	if err := r.ParseForm(); err != nil {
		panic(err)
	}

	var json_content interface{}
	if err := json.NewDecoder(r.Body).Decode(&json_content); err != nil {
		// ignore json parsing errors
	}

	fullUrl := r.URL
	fullUrl.Host = r.Host
	fullUrl.Scheme = "http" // TODO find a solution
	fullUrl.RawQuery = r.URL.Query().Encode()

	userAgentInfo, _ := GetUserAgentInfo(r)

	return HttpRequest{
		Request{
			Time:       time.Now(),
			RemoteAddr: r.RemoteAddr,
		},
		r.URL.Path,
		fullUrl.String(),
		r.Method,
		r.Header,
		string(body),
		r.Host,
		r.PostForm,
		r.Form,
		json_content,
		binId,
		userAgentInfo,
	}
}

func json_response(w http.ResponseWriter, body interface{}) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(body); err != nil {
		panic(err)
	}
}

func ApiBinHandler(redisClient redis.Conn) func(http.ResponseWriter, *http.Request) {

	return func(w http.ResponseWriter, r *http.Request) {
		bin := mux.Vars(r)["binId"]
		requests := ListRequestsFromBin(redisClient, bin)
		json_response(w, requests)
	}
}

func ApiBinIndexHandler(redisClient redis.Conn) func(http.ResponseWriter, *http.Request) {

	return func(w http.ResponseWriter, r *http.Request) {
		bins := ListBins(redisClient)
		json_response(w, bins)
	}
}

func HomeHandler(w http.ResponseWriter, r *http.Request) {
	params := struct {
		Title string
	}{Title: "Welcome to RequestBin"}

	getTemplate(w, "home").ExecuteTemplate(w, "base", params)
}

func BinHandler(redisClient redis.Conn) func(http.ResponseWriter, *http.Request) {

	return func(w http.ResponseWriter, r *http.Request) {
		binId := mux.Vars(r)["binId"]
		requests := ListRequestsFromBin(redisClient, binId)

		params := struct {
			Requests []HttpRequest
			Title    string
		}{
			Requests: requests,
			Title:    "Bin #" + binId,
		}

		err := getTemplate(w, "bin").ExecuteTemplate(w, "base", params)
		if err != nil {
			panic(err)
		}
	}
}

func RenderDocumentTemplate(writer io.Writer, source string, trackerUrl string) {
	t := template.Must(template.New("template").ParseFiles(source))
	params := struct{ Url string }{Url: trackerUrl}
	if err := t.ExecuteTemplate(writer, "document", params); err != nil {
		fmt.Println(err)
	}
}

func getTemplate(w http.ResponseWriter, tmpl string) *template.Template {
	funcMap := template.FuncMap{
		"lookup_addr": func(addr string) []string {
			names, err := net.LookupAddr(addr)
			if err != nil {
				return []string{addr}
			}
			return names
		},
	}
	templates := template.New("template").Funcs(funcMap)
	templates_folder := os.Getenv("ROOT") + "/templates/"
	_, err := templates.ParseFiles(templates_folder+"base.html", templates_folder+tmpl+".html")
	if err != nil {
		fmt.Println(err)
	}

	return template.Must(templates, err)
}

func LogHandler(handlers []HttpRequestHandler, writers []HttpRequestWriter) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		binId, ok := mux.Vars(r)["binId"]
		if !ok {
			binId = "_root"
		}
		request := ParseHttpRequest(r, binId)
		for _, writer := range writers {
			err := writer.WriteHttpRequest(request)
			if err != nil {
				fmt.Println(err)
			}
		}

		for _, handler := range handlers {
			if handler.Handle(w, r, &request) {
				return
			}
		}

		json_response(w, request)

	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math"

	"github.com/jzacsh/netwtcpip-cmp405/parseip4"
)

var partTwoHosts = []parseip4.Addr{
	{IP: parseip4.NewAddr(9, 201, 195, 84), Mask: parseip4.NewAddr(255, 255, 240, 0)},
	{IP: parseip4.NewAddr(128, 10, 189, 215), Mask: parseip4.NewAddr(255, 255, 248, 0)},
	{IP: parseip4.NewAddr(135, 21, 243, 82), Mask: parseip4.NewAddr(255, 255, 224, 0)},
	{IP: parseip4.NewAddr(75, 149, 205, 61), Mask: parseip4.NewAddr(255, 255, 192, 0)},
	{IP: parseip4.NewAddr(7, 105, 198, 111), Mask: parseip4.NewAddr(255, 255, 252, 0)},

	// TODO(zacsh) remove this sample from the last slide
	{IP: parseip4.NewAddr(128, 10, 211, 78), Mask: parseip4.NewAddr(255, 255, 240, 0)},
}

type subnetRequisites struct {
	MaxSubnets  uint
	SubnetIndex parseip4.Octets
	HostIndex   parseip4.Octets
}

type OptimalSubnet struct {
	MinSubnetBits     uint
	MaxHostsPerSubnet parseip4.Octets
	SubnetMask        parseip4.OctsList
}

var partOneGivens = []subnetRequisites{
	{55, 51, 121},
	{55, 42, 867},
	{121, 115, 246},
	{121, 97, 443},
	{26, 19, 237},
	{26, 25, 1397},
	{261, 227, 86},
	{261, 259, 49},
	{529, 519, 33},
	{529, 510, 59},
}

func (s *subnetRequisites) String() string {
	return fmt.Sprintf(
		"max subnets: %d, subnet index: %d, host index: %d",
		s.MaxSubnets, s.SubnetIndex, s.HostIndex)
}

func (s *subnetRequisites) FindSolution() OptimalSubnet {
	opt := OptimalSubnet{}

	// Brute force solve for Ceil(log2(s.MaxSubnets))
	for {
		if math.Pow(2, float64(opt.MinSubnetBits))-1 >= float64(s.MaxSubnets) {
			break
		}
		opt.MinSubnetBits++
	}

	// TODO: opt.MaxHostsPerSubnet

	// TODO: opt.SubnetMask

	return opt
}

func main() {
	fmt.Printf("part 1: analyzing %d hosts ...\n", len(partOneGivens))
	for _, req := range partOneGivens {
		sol := req.FindSolution()
		fmt.Printf(
			"  given: %s\n\tmin # of subnet bits: %d\n\tmax # hosts per subnet: %d\n\tsubnetmask: %d\n",
			req.String(),
			sol.MinSubnetBits,
			sol.MaxHostsPerSubnet,
			sol.SubnetMask)
	}

	fmt.Printf("\n\npart 2: analyzing %d hosts ...\n", len(partTwoHosts))
	for _, addr := range partTwoHosts {
		classMask, _, klass := parseip4.Classful(addr.IP)

		fmt.Printf(
			"  network: %v (class %s masked)\n\t%v\n\tnetwork id:\t%d\n\t subnet id:\t%d\n\t   host id:\t%d\n",
			(addr.IP.Pack() & classMask.Pack()).List(), klass,
			addr.String(),
			addr.NetworkIndex(),
			addr.SubnetIndex(),
			addr.HostIndex())
	}
}
<commit_msg>hw4(part1.2 of 1.3) calc max number of hosts per subnet<commit_after>package main

import (
	"fmt"
	"math"

	"github.com/jzacsh/netwtcpip-cmp405/parseip4"
)

var partTwoHosts = []parseip4.Addr{
	{IP: parseip4.NewAddr(9, 201, 195, 84), Mask: parseip4.NewAddr(255, 255, 240, 0)},
	{IP: parseip4.NewAddr(128, 10, 189, 215), Mask: parseip4.NewAddr(255, 255, 248, 0)},
	{IP: parseip4.NewAddr(135, 21, 243, 82), Mask: parseip4.NewAddr(255, 255, 224, 0)},
	{IP: parseip4.NewAddr(75, 149, 205, 61), Mask: parseip4.NewAddr(255, 255, 192, 0)},
	{IP: parseip4.NewAddr(7, 105, 198, 111), Mask: parseip4.NewAddr(255, 255, 252, 0)},

	// TODO(zacsh) remove this sample from the last slide
	{IP: parseip4.NewAddr(128, 10, 211, 78), Mask: parseip4.NewAddr(255, 255, 240, 0)},
}

type subnetRequisites struct {
	MaxSubnets  uint
	SubnetIndex parseip4.Octets
	HostIndex   parseip4.Octets
}

type OptimalSubnet struct {
	MinSubnetBits     uint
	MaxHostsPerSubnet parseip4.Octets
	SubnetMask        parseip4.OctsList
}

var partOneGivens = []subnetRequisites{
	{55, 51, 121},
	{55, 42, 867},
	{121, 115, 246},
	{121, 97, 443},
	{26, 19, 237},
	{26, 25, 1397},
	{261, 227, 86},
	{261, 259, 49},
	{529, 519, 33},
	{529, 510, 59},
}

func (s *subnetRequisites) String() string {
	return fmt.Sprintf(
		"max subnets: %d, subnet index: %d, host index: %d",
		s.MaxSubnets, s.SubnetIndex, s.HostIndex)
}

func maxIntWithBits(nbits uint) uint32 {
	// 1 because 2^N bits only gets 2^N-1 if all 1s. +1 more because all 1s is
	// reserved for broadcast.
	const gap float64 = 2

	maxInt := math.Pow(2, float64(nbits))
	if maxInt < gap {
		// we want to avoid underflows, so stick to the point of the API and return
		// effectively zero
		return 0
	}

	return uint32(maxInt - gap)
}

func (s *subnetRequisites) FindSolution() OptimalSubnet {
	opt := OptimalSubnet{}

	// Brute force solve for Ceil(log2(s.MaxSubnets))
	for {
		if maxIntWithBits(opt.MinSubnetBits) >= uint32(s.MaxSubnets) {
			break
		}
		opt.MinSubnetBits++
	}

	opt.MaxHostsPerSubnet = parseip4.Octets(maxIntWithBits(32 - opt.MinSubnetBits))

	// TODO: opt.SubnetMask

	return opt
}

func main() {
	fmt.Printf("part 1: analyzing %d hosts ...\n", len(partOneGivens))
	for _, req := range partOneGivens {
		sol := req.FindSolution()
		fmt.Printf(
			"  given: %s\n\tmin # of subnet bits: %d\n\tmax # hosts per subnet: %d\n\tsubnetmask: %d\n",
			req.String(),
			sol.MinSubnetBits,
			sol.MaxHostsPerSubnet,
			sol.SubnetMask)
	}

	fmt.Printf("\n\npart 2: analyzing %d hosts ...\n", len(partTwoHosts))
	for _, addr := range partTwoHosts {
		classMask, _, klass := parseip4.Classful(addr.IP)

		fmt.Printf(
			"  network: %v (class %s masked)\n\t%v\n\tnetwork id:\t%d\n\t subnet id:\t%d\n\t   host id:\t%d\n",
			(addr.IP.Pack() & classMask.Pack()).List(), klass,
			addr.String(),
			addr.NetworkIndex(),
			addr.SubnetIndex(),
			addr.HostIndex())
	}
}
<|endoftext|>


<commit_before>//the icws_golib package wraps CIC ICWS fuctionality in a GO library.
//To get started, call icws_golib.NewIcws() to instansiate a new Icws
//struct.  Then call icws.Login(...) and continue on from there.
package icws_golib

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
)

type Icws struct {
	CurrentToken, CurrentCookie, CurrentSession, CurrentServer, UserId string

}

//Version information for the server
type ServerVersion struct{
	//The product's two-digit release year. For the release "CIC 2015 R1" this value will be "15"
	MajorVersion string
	//The product's release number. For the release "CIC 2015 R1" this value will be "1".
	MinorVersion string
	//The patch number of the release. The value "0" indicates the release without any patches. For the release "CIC 2015 R1" this value will be "0", and for the release "CIC 2015 R1 Patch2" this value will be "2".
	Su string
	//The product line identifier.
	ProductId string
	//The codebase identifier.
	CodeBaseId string
	//The build number.
	Build string
	//The display string for the release. This does not include patch information.
	ProductReleaseDisplayString string
	//The display string for the release including patch information. This string is recommended for use on application "About" screens.
	ProductPatchDisplayString string
}

//Definition of a server feature
type ServerFeature struct{
	//Id of the features
	FeatureId string
	//version of the feature
	Version int
}

//Creates a new ICWS struct
func NewIcws() (icws *Icws) {
	icws = &Icws{}
	return
}

func (i *Icws) loginWithData(applicationName, server, username, password string, loginData map[string]string) (err error) {

	server = fmt.Sprintf("http://%s:8018", server)

	log.Printf("Logging into %s with user %s", server, username)

	i.CurrentSession = ""
	i.CurrentServer = server
	body, statusCode, err, cookie := i.httpPostAdvanced("connection", loginData)

	nextServerIndex := 0
	for statusCode == 503 {

		var returnData map[string]interface{}
		json.Unmarshal(body, &returnData)

		nextServer := returnData["alternateHostList"].([]interface{})[0]

		server = fmt.Sprintf("http://%s:8018", nextServer)

		log.Printf("Redirected to server %s", server)

		i.CurrentServer = server
		body, statusCode, err, cookie = i.httpPostAdvanced("connection", loginData)

		nextServerIndex++
	}

	if err == nil {

		var returnData map[string]string
		json.Unmarshal(body, &returnData)
		i.CurrentToken = returnData["csrfToken"]
		i.CurrentSession = returnData["sessionId"]
		i.CurrentServer = server
		i.CurrentCookie = cookie
		i.UserId = username

	} else {
		log.Printf("ERROR: %s\n", err.Error())
	}
	return
}

//Logs into a CIC server.  Server should be a server name e.g. MyServer.domain.com
func (i *Icws) Login(applicationName, server, username, password string) (err error) {

	var loginData = map[string]string{
		"__type":          "urn:inin.com:connection:icAuthConnectionRequestSettings",
		"applicationName": applicationName,
		"userID":          username,
		"password":        password,
	}

	return i.loginWithData(applicationName, server, username, password, loginData)
}

//Logs into a CIC server for a MarketPlace application using the app's custom license.  Server should be a url e.g. https://MyServer:8019
func (i *Icws) LoginMarketPlaceApp(applicationName, server, username, password, marketplaceLicense, markeplaceAppKey string) (err error) {

	var loginData = map[string]string{
		"__type":          "urn:inin.com:connection:icAuthConnectionRequestSettings",
		"applicationName": applicationName,
		"userID":          username,
		"password":        password,
	}

	return i.loginWithData(applicationName, server, username, password, loginData)
}

//gets server version details.  Does not need to be connected to the server
func (i *Icws) GetVersion() (version ServerVersion, err error) {

	body, err := i.httpNoConnectionGet("/connection/version")

	err = json.Unmarshal(body, &version)

	return
}

//Gets a list of features and their version from the server
func (i *Icws) GetFeatures() (features []ServerFeature, err error) {

	body, err := i.httpNoConnectionGet("/connection/features")

	if err != nil {
		return nil, err
	}

	var featureMap map[string][]ServerFeature
	err = json.Unmarshal(body, &featureMap)

	features = featureMap["featureInfoList"]
	return

}

func (i *Icws) httpDelete(url string) (err error) {

	req, err := i.httpRequest("DELETE", url, nil)

	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return
	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, _ := ioutil.ReadAll(response.Body)
	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpGet(url string) (body []byte, err error) {

	req, err := i.httpRequest("GET", url, nil)

	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return
	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)
	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpNoConnectionGet(url string) (body []byte, err error) {

	req, err := i.httpNoConnectionRequest("GET", url, nil)

	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return
	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)
	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpPost(url string, attrs map[string]string) (body []byte, err error) {
	body, _, err, _ = i.httpPostAdvanced(url, attrs)
	return
}

func (i *Icws) httpPostAdvanced(url string, attrs map[string]string) (body []byte, statusCode int, err error, cookie string) {

	rbody, _ := json.Marshal(attrs)
	req, err := i.httpRequest("POST", url, bytes.NewReader(rbody))
	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return

	}
	defer response.Body.Close()

	statusCode = response.StatusCode

	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)

	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	if response.Header["Set-Cookie"] != nil {
		cookie = response.Header["Set-Cookie"][0]
	}
	return
}

func (i *Icws) httpPut(url string, attrs map[string]string) (body []byte, err error) {

	rbody, _ := json.Marshal(attrs)
	req, err := i.httpRequest("PUT", url, bytes.NewReader(rbody))
	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return

	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)

	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpClient() (client *http.Client) {

	client = &http.Client{}
	return
}

func (i *Icws) httpNoConnectionRequest(method, url string, body io.Reader) (request *http.Request, err error) {
	request, err = http.NewRequest(method, i.CurrentServer+"/icws"+url, body)
	if err != nil {
		return
	}

	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("Accept-Language", "en-us")

	return
}

func (i *Icws) httpRequest(method, url string, body io.Reader) (request *http.Request, err error) {
	request, err = http.NewRequest(method, i.CurrentServer+"/icws/"+i.CurrentSession+url, body)
	if err != nil {
		return
	}

	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("Accept-Language", "en-us")

	if len(i.CurrentCookie) > 0 {
		request.Header.Add("Cookie", i.CurrentCookie)
	} else {
		return
	}

	if len(i.CurrentToken) > 0 {
		request.Header.Add("ININ-ICWS-CSRF-Token", i.CurrentToken)
	} else {
		return
	}

	// request.Header.Add("User-Agent", fmt.Sprintf("cic cli (%s-%s)", runtime.GOOS, runtime.GOARCH))
	return
}

func createErrorMessage(statusCode int, body []byte) string {

	var errorDescription string

	switch statusCode {
	case 400:
		errorDescription = "Bad Request (400)"
	case 401:
		errorDescription = "Unauthorized (401)"
	case 403:
		errorDescription = "Forbidden (403)"
	case 404:
		errorDescription = "Not Found (404)"
	case 410:
		errorDescription = "Gone (410)"
	case 500:
		errorDescription = "Internal Server Error (500)"
	}

	var message map[string]interface{}
	json.Unmarshal(body, &message)

	if message["errorId"] != nil {
		errorDescription += ": " + message["errorId"].(string)
	}

	if message["message"] != nil {
		errorDescription += " " + message["message"].(string)
	}
	return errorDescription
}
<commit_msg>changing icws protocol to https<commit_after>//the icws_golib package wraps CIC ICWS fuctionality in a GO library.
//To get started, call icws_golib.NewIcws() to instansiate a new Icws
//struct.  Then call icws.Login(...) and continue on from there.
package icws_golib

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"crypto/tls"
)

type Icws struct {
	CurrentToken, CurrentCookie, CurrentSession, CurrentServer, UserId string

}

//Version information for the server
type ServerVersion struct{
	//The product's two-digit release year. For the release "CIC 2015 R1" this value will be "15"
	MajorVersion string
	//The product's release number. For the release "CIC 2015 R1" this value will be "1".
	MinorVersion string
	//The patch number of the release. The value "0" indicates the release without any patches. For the release "CIC 2015 R1" this value will be "0", and for the release "CIC 2015 R1 Patch2" this value will be "2".
	Su string
	//The product line identifier.
	ProductId string
	//The codebase identifier.
	CodeBaseId string
	//The build number.
	Build string
	//The display string for the release. This does not include patch information.
	ProductReleaseDisplayString string
	//The display string for the release including patch information. This string is recommended for use on application "About" screens.
	ProductPatchDisplayString string
}

//Definition of a server feature
type ServerFeature struct{
	//Id of the features
	FeatureId string
	//version of the feature
	Version int
}

//Creates a new ICWS struct
func NewIcws() (icws *Icws) {
	icws = &Icws{}
	return
}

func (i *Icws) loginWithData(applicationName, server, username, password string, loginData map[string]string) (err error) {

	server = fmt.Sprintf("https://%s:8019", server)

	log.Printf("Logging into %s with user %s", server, username)

	i.CurrentSession = ""
	i.CurrentServer = server
	body, statusCode, err, cookie := i.httpPostAdvanced("connection", loginData)

	nextServerIndex := 0
	for statusCode == 503 {

		var returnData map[string]interface{}
		json.Unmarshal(body, &returnData)

		nextServer := returnData["alternateHostList"].([]interface{})[0]

		server = fmt.Sprintf("http://%s:8018", nextServer)

		log.Printf("Redirected to server %s", server)

		i.CurrentServer = server
		body, statusCode, err, cookie = i.httpPostAdvanced("connection", loginData)

		nextServerIndex++
	}

	if err == nil {

		var returnData map[string]string
		json.Unmarshal(body, &returnData)
		i.CurrentToken = returnData["csrfToken"]
		i.CurrentSession = returnData["sessionId"]
		i.CurrentServer = server
		i.CurrentCookie = cookie
		i.UserId = username

	} else {
		log.Printf("ERROR: %s\n", err.Error())
	}
	return
}

//Logs into a CIC server.  Server should be a server name e.g. MyServer.domain.com
func (i *Icws) Login(applicationName, server, username, password string) (err error) {

	var loginData = map[string]string{
		"__type":          "urn:inin.com:connection:icAuthConnectionRequestSettings",
		"applicationName": applicationName,
		"userID":          username,
		"password":        password,
	}

	return i.loginWithData(applicationName, server, username, password, loginData)
}

//Logs into a CIC server for a MarketPlace application using the app's custom license.  Server should be a url e.g. https://MyServer:8019
func (i *Icws) LoginMarketPlaceApp(applicationName, server, username, password, marketplaceLicense, markeplaceAppKey string) (err error) {

	var loginData = map[string]string{
		"__type":          "urn:inin.com:connection:icAuthConnectionRequestSettings",
		"applicationName": applicationName,
		"userID":          username,
		"password":        password,
	}

	return i.loginWithData(applicationName, server, username, password, loginData)
}

//gets server version details.  Does not need to be connected to the server
func (i *Icws) GetVersion() (version ServerVersion, err error) {

	body, err := i.httpNoConnectionGet("/connection/version")

	err = json.Unmarshal(body, &version)

	return
}

//Gets a list of features and their version from the server
func (i *Icws) GetFeatures() (features []ServerFeature, err error) {

	body, err := i.httpNoConnectionGet("/connection/features")

	if err != nil {
		return nil, err
	}

	var featureMap map[string][]ServerFeature
	err = json.Unmarshal(body, &featureMap)

	features = featureMap["featureInfoList"]
	return

}

func (i *Icws) httpDelete(url string) (err error) {

	req, err := i.httpRequest("DELETE", url, nil)

	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return
	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, _ := ioutil.ReadAll(response.Body)
	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpGet(url string) (body []byte, err error) {

	req, err := i.httpRequest("GET", url, nil)

	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return
	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)
	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpNoConnectionGet(url string) (body []byte, err error) {

	req, err := i.httpNoConnectionRequest("GET", url, nil)

	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return
	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)
	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpPost(url string, attrs map[string]string) (body []byte, err error) {
	body, _, err, _ = i.httpPostAdvanced(url, attrs)
	return
}

func (i *Icws) httpPostAdvanced(url string, attrs map[string]string) (body []byte, statusCode int, err error, cookie string) {

	rbody, _ := json.Marshal(attrs)
	req, err := i.httpRequest("POST", url, bytes.NewReader(rbody))
	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return

	}
	defer response.Body.Close()

	statusCode = response.StatusCode

	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)

	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	if response.Header["Set-Cookie"] != nil {
		cookie = response.Header["Set-Cookie"][0]
	}
	return
}

func (i *Icws) httpPut(url string, attrs map[string]string) (body []byte, err error) {

	rbody, _ := json.Marshal(attrs)
	req, err := i.httpRequest("PUT", url, bytes.NewReader(rbody))
	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return

	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)

	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpClient() (client *http.Client) {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify : true},
	}
	client = &http.Client{Transport: tr}
	return
}

func (i *Icws) httpNoConnectionRequest(method, url string, body io.Reader) (request *http.Request, err error) {
	request, err = http.NewRequest(method, i.CurrentServer+"/icws"+url, body)
	if err != nil {
		return
	}

	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("Accept-Language", "en-us")

	return
}

func (i *Icws) httpRequest(method, url string, body io.Reader) (request *http.Request, err error) {
	request, err = http.NewRequest(method, i.CurrentServer+"/icws/"+i.CurrentSession+url, body)
	if err != nil {
		return
	}

	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("Accept-Language", "en-us")

	if len(i.CurrentCookie) > 0 {
		request.Header.Add("Cookie", i.CurrentCookie)
	} else {
		return
	}

	if len(i.CurrentToken) > 0 {
		request.Header.Add("ININ-ICWS-CSRF-Token", i.CurrentToken)
	} else {
		return
	}

	// request.Header.Add("User-Agent", fmt.Sprintf("cic cli (%s-%s)", runtime.GOOS, runtime.GOARCH))
	return
}

func createErrorMessage(statusCode int, body []byte) string {

	var errorDescription string

	switch statusCode {
	case 400:
		errorDescription = "Bad Request (400)"
	case 401:
		errorDescription = "Unauthorized (401)"
	case 403:
		errorDescription = "Forbidden (403)"
	case 404:
		errorDescription = "Not Found (404)"
	case 410:
		errorDescription = "Gone (410)"
	case 500:
		errorDescription = "Internal Server Error (500)"
	}

	var message map[string]interface{}
	json.Unmarshal(body, &message)

	if message["errorId"] != nil {
		errorDescription += ": " + message["errorId"].(string)
	}

	if message["message"] != nil {
		errorDescription += " " + message["message"].(string)
	}
	return errorDescription
}
<|endoftext|>