<commit_before>// Copyright (C) 2014 Space Monkey, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build cgo

package openssl

// #cgo pkg-config: libssl
// #cgo windows CFLAGS: -DWIN32_LEAN_AND_MEAN
import "C"
<commit_msg>Hide deprecated declarations on OSX<commit_after>// Copyright (C) 2014 Space Monkey, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build cgo

package openssl

// #cgo pkg-config: libssl
// #cgo windows CFLAGS: -DWIN32_LEAN_AND_MEAN
// #cgo darwin CFLAGS: -Wno-deprecated-declarations
import "C"
<|endoftext|>


<commit_before>package unix_transport

import (
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httptest"
	"strings"

	"github.com/nu7hatch/gouuid"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/ghttp"
)

var _ = Describe("Unix transport", func() {

	var (
		socket string
		client http.Client
	)

	Context("with server listening", func() {

		var (
			unixSocketListener net.Listener
			unixSocketServer   *ghttp.Server
			resp               *http.Response
			err                error
		)

		BeforeEach(func() {
			uuid, err := uuid.NewV4()
			Expect(err).NotTo(HaveOccurred())

			socket = fmt.Sprintf("/tmp/%s.sock", uuid)
			unixSocketListener, err = net.Listen("unix", socket)
			Expect(err).NotTo(HaveOccurred())

			unixSocketServer = ghttp.NewUnstartedServer()

			unixSocketServer.HTTPTestServer = &httptest.Server{
				Listener: unixSocketListener,
				Config:   &http.Server{Handler: unixSocketServer},
			}
			unixSocketServer.Start()

			client = http.Client{Transport: New(socket)}
		})

		Context("when a simple GET request is sent", func() {
			BeforeEach(func() {
				unixSocketServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", "/_ping"),
						ghttp.RespondWith(http.StatusOK, "true"),
					),
				)

				resp, err = client.Get("unix://" + socket + "/_ping")
			})

			It("responds with correct status", func() {
				Expect(err).NotTo(HaveOccurred())
				Expect(resp.StatusCode).To(Equal(http.StatusOK))

			})

			It("responds with correct body", func() {
				bytes, err := ioutil.ReadAll(resp.Body)
				Expect(err).NotTo(HaveOccurred())
				Expect(string(bytes)).To(Equal("true"))
			})
		})

		Context("when a POST request is sent", func() {
			const (
				ReqBody  = `"id":"some-id"`
				RespBody = `{"Image" : "ubuntu"}`
			)

			assertBodyEquals := func(body io.ReadCloser, expectedContent string) {
				bytes, err := ioutil.ReadAll(body)
				Expect(err).NotTo(HaveOccurred())
				Expect(string(bytes)).To(Equal(expectedContent))

			}

			asserHeaderContains := func(header http.Header, key, value string) {
				Expect(header[key]).To(ConsistOf(value))
			}

			BeforeEach(func() {
				validateBody := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
					assertBodyEquals(req.Body, ReqBody)
				})

				validateQueryParams := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
					Expect(req.URL.RawQuery).To(Equal("fromImage=ubunut&tag=latest"))
				})

				handleRequest := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
					w.Header().Set("Content-Type", "application/json")
					w.Write([]byte(RespBody))
				})

				unixSocketServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("POST", "/containers/create"),
						ghttp.VerifyContentType("application/json"),
						validateBody,
						validateQueryParams,
						handleRequest,
					),
				)
				body := strings.NewReader(ReqBody)
				req, err := http.NewRequest("POST", "unix://"+socket+"/containers/create?fromImage=ubunut&tag=latest", body)
				req.Header.Add("Content-Type", "application/json")
				Expect(err).NotTo(HaveOccurred())

				resp, err = client.Do(req)
				Expect(err).NotTo(HaveOccurred())

			})

			It("responds with correct status", func() {
				Expect(resp.StatusCode).To(Equal(http.StatusOK))
			})

			It("responds with correct headers", func() {
				asserHeaderContains(resp.Header, "Content-Type", "application/json")
			})

			It("responds with correct body", func() {
				assertBodyEquals(resp.Body, RespBody)
			})

		})

		Context("when socket in reques URI is incorrect", func() {
			It("errors", func() {
				resp, err = client.Get("unix:///fake/socket.sock/_ping")
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(ContainSubstring("Wrong unix socket"))
			})
		})

		AfterEach(func() {
			unixSocketServer.Close()
		})
	})

	Context("with no server listening", func() {
		BeforeEach(func() {
			socket = "/not/existing.sock"
			client = http.Client{Transport: New(socket)}
		})

		It("errors", func() {
			_, err := client.Get("unix:///not/existing.sock/_ping")
			Expect(err).To(HaveOccurred())
			Expect(err.Error()).To(Or(
				ContainSubstring(fmt.Sprintf("dial unix %s: connect: no such file or directory", socket)),
				ContainSubstring(fmt.Sprintf("dial unix %s: no such file or directory", socket)),
			))
		})
	})
})
<commit_msg>Skip Unix transport tests on Windows.<commit_after>package unix_transport

import (
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httptest"
	"runtime"
	"strings"

	"github.com/nu7hatch/gouuid"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/ghttp"
)

var _ = Describe("Unix transport", func() {

	var (
		socket string
		client http.Client
	)

	BeforeEach(func() {
		if runtime.GOOS == "windows" {
			Skip("Skipping Unix transport tests on Windows")
		}
	})

	Context("with server listening", func() {

		var (
			unixSocketListener net.Listener
			unixSocketServer   *ghttp.Server
			resp               *http.Response
			err                error
		)

		BeforeEach(func() {
			uuid, err := uuid.NewV4()
			Expect(err).NotTo(HaveOccurred())

			socket = fmt.Sprintf("/tmp/%s.sock", uuid)
			unixSocketListener, err = net.Listen("unix", socket)
			Expect(err).NotTo(HaveOccurred())

			unixSocketServer = ghttp.NewUnstartedServer()

			unixSocketServer.HTTPTestServer = &httptest.Server{
				Listener: unixSocketListener,
				Config:   &http.Server{Handler: unixSocketServer},
			}
			unixSocketServer.Start()

			client = http.Client{Transport: New(socket)}
		})

		Context("when a simple GET request is sent", func() {
			BeforeEach(func() {
				unixSocketServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("GET", "/_ping"),
						ghttp.RespondWith(http.StatusOK, "true"),
					),
				)

				resp, err = client.Get("unix://" + socket + "/_ping")
			})

			It("responds with correct status", func() {
				Expect(err).NotTo(HaveOccurred())
				Expect(resp.StatusCode).To(Equal(http.StatusOK))

			})

			It("responds with correct body", func() {
				bytes, err := ioutil.ReadAll(resp.Body)
				Expect(err).NotTo(HaveOccurred())
				Expect(string(bytes)).To(Equal("true"))
			})
		})

		Context("when a POST request is sent", func() {
			const (
				ReqBody  = `"id":"some-id"`
				RespBody = `{"Image" : "ubuntu"}`
			)

			assertBodyEquals := func(body io.ReadCloser, expectedContent string) {
				bytes, err := ioutil.ReadAll(body)
				Expect(err).NotTo(HaveOccurred())
				Expect(string(bytes)).To(Equal(expectedContent))

			}

			asserHeaderContains := func(header http.Header, key, value string) {
				Expect(header[key]).To(ConsistOf(value))
			}

			BeforeEach(func() {
				validateBody := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
					assertBodyEquals(req.Body, ReqBody)
				})

				validateQueryParams := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
					Expect(req.URL.RawQuery).To(Equal("fromImage=ubunut&tag=latest"))
				})

				handleRequest := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
					w.Header().Set("Content-Type", "application/json")
					w.Write([]byte(RespBody))
				})

				unixSocketServer.AppendHandlers(
					ghttp.CombineHandlers(
						ghttp.VerifyRequest("POST", "/containers/create"),
						ghttp.VerifyContentType("application/json"),
						validateBody,
						validateQueryParams,
						handleRequest,
					),
				)
				body := strings.NewReader(ReqBody)
				req, err := http.NewRequest("POST", "unix://"+socket+"/containers/create?fromImage=ubunut&tag=latest", body)
				req.Header.Add("Content-Type", "application/json")
				Expect(err).NotTo(HaveOccurred())

				resp, err = client.Do(req)
				Expect(err).NotTo(HaveOccurred())

			})

			It("responds with correct status", func() {
				Expect(resp.StatusCode).To(Equal(http.StatusOK))
			})

			It("responds with correct headers", func() {
				asserHeaderContains(resp.Header, "Content-Type", "application/json")
			})

			It("responds with correct body", func() {
				assertBodyEquals(resp.Body, RespBody)
			})

		})

		Context("when socket in reques URI is incorrect", func() {
			It("errors", func() {
				resp, err = client.Get("unix:///fake/socket.sock/_ping")
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(ContainSubstring("Wrong unix socket"))
			})
		})

		AfterEach(func() {
			unixSocketServer.Close()
		})
	})

	Context("with no server listening", func() {
		BeforeEach(func() {
			socket = "/not/existing.sock"
			client = http.Client{Transport: New(socket)}
		})

		It("errors", func() {
			_, err := client.Get("unix:///not/existing.sock/_ping")
			Expect(err).To(HaveOccurred())
			Expect(err.Error()).To(Or(
				ContainSubstring(fmt.Sprintf("dial unix %s: connect: no such file or directory", socket)),
				ContainSubstring(fmt.Sprintf("dial unix %s: no such file or directory", socket)),
			))
		})
	})
})
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/russross/blackfriday"
	log "github.com/sirupsen/logrus"
	"github.com/yuin/gopher-lua"
)

func exportBasicSystemFunctions(L *lua.LState) {

	// Return the version string
	L.SetGlobal("version", L.NewFunction(func(L *lua.LState) int {
		L.Push(lua.LString(versionString))
		return 1 // number of results
	}))

	// Log text with the "Info" log type
	L.SetGlobal("log", L.NewFunction(func(L *lua.LState) int {
		buf := arguments2buffer(L, false)
		// Log the combined text
		log.Info(buf.String())
		return 0 // number of results
	}))

	// Log text with the "Warn" log type
	L.SetGlobal("warn", L.NewFunction(func(L *lua.LState) int {
		buf := arguments2buffer(L, false)
		// Log the combined text
		log.Warn(buf.String())
		return 0 // number of results
	}))

	// Log text with the "Error" log type
	L.SetGlobal("err", L.NewFunction(func(L *lua.LState) int {
		buf := arguments2buffer(L, false)
		// Log the combined text
		log.Error(buf.String())
		return 0 // number of results
	}))

	// Sleep for the given number of seconds (can be a float)
	L.SetGlobal("sleep", L.NewFunction(func(L *lua.LState) int {
		// Extract the correct number of nanoseconds
		duration := time.Duration(float64(L.ToNumber(1)) * 1000000000.0)
		// Wait and block the current thread of execution.
		time.Sleep(duration)
		return 0
	}))

	// Return the current unixtime, with an attempt at nanosecond resolution
	L.SetGlobal("unixnano", L.NewFunction(func(L *lua.LState) int {
		// Extract the correct number of nanoseconds
		L.Push(lua.LNumber(time.Now().UnixNano()))
		return 1 // number of results
	}))

	// Convert Markdown to HTML
	L.SetGlobal("markdown", L.NewFunction(func(L *lua.LState) int {
		// Retrieve all the function arguments as a bytes.Buffer
		buf := arguments2buffer(L, true)
		// Convert the buffer to markdown and output the translated string
		html := strings.TrimSpace(string(blackfriday.MarkdownCommon([]byte(buf.String()))))
		L.Push(lua.LString(html))
		return 1 // number of results
	}))

	// Get the full filename of a given file that is in the directory
	// where the server is running (root directory for the server).
	// If no filename is given, the directory where the server is
	// currently running is returned.
	L.SetGlobal("serverdir", L.NewFunction(func(L *lua.LState) int {
		serverdir, err := os.Getwd()
		if err != nil {
			// Could not retrieve a directory
			serverdir = ""
		} else if L.GetTop() == 1 {
			// Also include a separator and a filename
			fn := L.ToString(1)
			serverdir = filepath.Join(serverdir, fn)
		}
		L.Push(lua.LString(serverdir))
		return 1 // number of results
	}))

}

// Make functions related to HTTP requests and responses available to Lua scripts.
// Filename can be an empty string.
// realW is only used for flushing the buffer in debug mode, and is the underlying ResponseWriter.
func exportBasicWeb(w http.ResponseWriter, req *http.Request, L *lua.LState, filename string, flushFunc func()) {

	// Print text to the webpage that is being served. Add a newline.
	L.SetGlobal("print", L.NewFunction(func(L *lua.LState) int {
		var buf bytes.Buffer
		top := L.GetTop()
		for i := 1; i <= top; i++ {
			buf.WriteString(L.Get(i).String())
			if i != top {
				buf.WriteString("\t")
			}
		}
		// Final newline
		buf.WriteString("\n")

		// Write the combined text to the http.ResponseWriter
		w.Write(buf.Bytes())

		return 0 // number of results
	}))

	// Flush the ResponseWriter.
	// Needed in debug mode, where ResponseWriter is buffered.
	L.SetGlobal("flush", L.NewFunction(func(L *lua.LState) int {
		if flushFunc != nil {
			flushFunc()
		}
		return 0 // number of results
	}))

	// Set the Content-Type for the page
	L.SetGlobal("content", L.NewFunction(func(L *lua.LState) int {
		lv := L.ToString(1)
		w.Header().Add("Content-Type", lv)
		return 0 // number of results
	}))

	// Return the current URL Path
	L.SetGlobal("urlpath", L.NewFunction(func(L *lua.LState) int {
		L.Push(lua.LString(req.URL.Path))
		return 1 // number of results
	}))

	// Return the current HTTP method (GET, POST etc)
	L.SetGlobal("method", L.NewFunction(func(L *lua.LState) int {
		L.Push(lua.LString(req.Method))
		return 1 // number of results
	}))

	// Return the HTTP headers as a table
	L.SetGlobal("headers", L.NewFunction(func(L *lua.LState) int {
		luaTable := L.NewTable()
		for key := range req.Header {
			L.RawSet(luaTable, lua.LString(key), lua.LString(req.Header.Get(key)))
		}
		L.Push(luaTable)
		return 1 // number of results
	}))

	// Return the HTTP header in the request, for a given key/string
	L.SetGlobal("header", L.NewFunction(func(L *lua.LState) int {
		key := L.ToString(1)
		value := req.Header.Get(key)
		L.Push(lua.LString(value))
		return 1 // number of results
	}))

	// Set the HTTP header in the request, for a given key and value
	L.SetGlobal("setheader", L.NewFunction(func(L *lua.LState) int {
		key := L.ToString(1)
		value := L.ToString(2)
		w.Header().Set(key, value)
		return 0 // number of results
	}))

	// Return the HTTP body in the request
	L.SetGlobal("body", L.NewFunction(func(L *lua.LState) int {
		body, err := ioutil.ReadAll(req.Body)
		var result lua.LString
		if err != nil {
			result = lua.LString("")
		} else {
			result = lua.LString(string(body))
		}
		L.Push(result)
		return 1 // number of results
	}))

	// Set the HTTP status code (must come before print)
	L.SetGlobal("status", L.NewFunction(func(L *lua.LState) int {
		code := int(L.ToNumber(1))
		w.WriteHeader(code)
		return 0 // number of results
	}))

	// Set a HTTP status code and print a message (optional)
	L.SetGlobal("error", L.NewFunction(func(L *lua.LState) int {
		code := int(L.ToNumber(1))
		w.WriteHeader(code)
		if L.GetTop() == 2 {
			message := L.ToString(2)
			fmt.Fprint(w, message)
		}
		return 0 // number of results
	}))

	// Get the full filename of a given file that is in the directory
	// of the script that is about to be run. If no filename is given,
	// the directory of the script is returned.
	L.SetGlobal("scriptdir", L.NewFunction(func(L *lua.LState) int {
		scriptpath, err := filepath.Abs(filename)
		if err != nil {
			scriptpath = filename
		}
		scriptdir := filepath.Dir(scriptpath)
		scriptpath = scriptdir
		top := L.GetTop()
		if top == 1 {
			// Also include a separator and a filename
			fn := L.ToString(1)
			scriptpath = filepath.Join(scriptdir, fn)
		}
		// Now have the correct absolute scriptpath
		L.Push(lua.LString(scriptpath))
		return 1 // number of results
	}))

	// Given a filename, return the URL path
	L.SetGlobal("file2url", L.NewFunction(func(L *lua.LState) int {
		fn := L.ToString(1)
		targetpath := filepath.Join(filepath.Dir(filename), fn)
		if strings.HasPrefix(targetpath, serverDir) {
			targetpath = targetpath[len(serverDir):]
		}
		if pathsep != "/" {
			// For operating systems that use another path separator for files than for URLs
			targetpath = strings.Replace(targetpath, pathsep, "/", everyInstance)
		}
		withSlashPrefix := path.Join("/", targetpath)
		L.Push(lua.LString(withSlashPrefix))
		return 1 // number of results
	}))

	// Retrieve a table with keys and values from the form in the request
	L.SetGlobal("formdata", L.NewFunction(func(L *lua.LState) int {
		// Place the form data in a map
		m := make(map[string]string)
		req.ParseForm()
		for key, values := range req.Form {
			m[key] = values[0]
		}
		// Convert the map to a table and return it
		L.Push(map2table(L, m))
		return 1 // number of results
	}))

	// Retrieve a table with keys and values from the URL in the request
	L.SetGlobal("urldata", L.NewFunction(func(L *lua.LState) int {

		var valueMap url.Values
		var err error

		if L.GetTop() == 1 {
			// If given an argument
			rawurl := L.ToString(1)
			valueMap, err = url.ParseQuery(rawurl)
			// Log error as warning if there are issues.
			// An empty Value map will then be used.
			if err != nil {
				log.Error(err)
				// return 0
			}
		} else {
			// If not given an argument
			valueMap = req.URL.Query() // map[string][]string
		}

		// Place the Value data in a map, using the first values
		// if there are many values for a given key.
		m := make(map[string]string)
		for key, values := range valueMap {
			m[key] = values[0]
		}
		// Convert the map to a table and return it
		L.Push(map2table(L, m))
		return 1 // number of results
	}))

	// Redirect a request
	L.SetGlobal("redirect", L.NewFunction(func(L *lua.LState) int {
		newurl := L.ToString(1)
		httpStatusCode := http.StatusMovedPermanently
		if L.GetTop() == 2 {
			if intStatusCode, err := strconv.Atoi(L.ToString(2)); err == nil {
				httpStatusCode = intStatusCode
			}
		}
		http.Redirect(w, req, newurl, httpStatusCode)
		return 0 // number of results
	}))

}
<commit_msg>Change the default redirect code to StatusFound<commit_after>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/russross/blackfriday"
	log "github.com/sirupsen/logrus"
	"github.com/yuin/gopher-lua"
)

func exportBasicSystemFunctions(L *lua.LState) {

	// Return the version string
	L.SetGlobal("version", L.NewFunction(func(L *lua.LState) int {
		L.Push(lua.LString(versionString))
		return 1 // number of results
	}))

	// Log text with the "Info" log type
	L.SetGlobal("log", L.NewFunction(func(L *lua.LState) int {
		buf := arguments2buffer(L, false)
		// Log the combined text
		log.Info(buf.String())
		return 0 // number of results
	}))

	// Log text with the "Warn" log type
	L.SetGlobal("warn", L.NewFunction(func(L *lua.LState) int {
		buf := arguments2buffer(L, false)
		// Log the combined text
		log.Warn(buf.String())
		return 0 // number of results
	}))

	// Log text with the "Error" log type
	L.SetGlobal("err", L.NewFunction(func(L *lua.LState) int {
		buf := arguments2buffer(L, false)
		// Log the combined text
		log.Error(buf.String())
		return 0 // number of results
	}))

	// Sleep for the given number of seconds (can be a float)
	L.SetGlobal("sleep", L.NewFunction(func(L *lua.LState) int {
		// Extract the correct number of nanoseconds
		duration := time.Duration(float64(L.ToNumber(1)) * 1000000000.0)
		// Wait and block the current thread of execution.
		time.Sleep(duration)
		return 0
	}))

	// Return the current unixtime, with an attempt at nanosecond resolution
	L.SetGlobal("unixnano", L.NewFunction(func(L *lua.LState) int {
		// Extract the correct number of nanoseconds
		L.Push(lua.LNumber(time.Now().UnixNano()))
		return 1 // number of results
	}))

	// Convert Markdown to HTML
	L.SetGlobal("markdown", L.NewFunction(func(L *lua.LState) int {
		// Retrieve all the function arguments as a bytes.Buffer
		buf := arguments2buffer(L, true)
		// Convert the buffer to markdown and output the translated string
		html := strings.TrimSpace(string(blackfriday.MarkdownCommon([]byte(buf.String()))))
		L.Push(lua.LString(html))
		return 1 // number of results
	}))

	// Get the full filename of a given file that is in the directory
	// where the server is running (root directory for the server).
	// If no filename is given, the directory where the server is
	// currently running is returned.
	L.SetGlobal("serverdir", L.NewFunction(func(L *lua.LState) int {
		serverdir, err := os.Getwd()
		if err != nil {
			// Could not retrieve a directory
			serverdir = ""
		} else if L.GetTop() == 1 {
			// Also include a separator and a filename
			fn := L.ToString(1)
			serverdir = filepath.Join(serverdir, fn)
		}
		L.Push(lua.LString(serverdir))
		return 1 // number of results
	}))

}

// Make functions related to HTTP requests and responses available to Lua scripts.
// Filename can be an empty string.
// realW is only used for flushing the buffer in debug mode, and is the underlying ResponseWriter.
func exportBasicWeb(w http.ResponseWriter, req *http.Request, L *lua.LState, filename string, flushFunc func()) {

	// Print text to the webpage that is being served. Add a newline.
	L.SetGlobal("print", L.NewFunction(func(L *lua.LState) int {
		var buf bytes.Buffer
		top := L.GetTop()
		for i := 1; i <= top; i++ {
			buf.WriteString(L.Get(i).String())
			if i != top {
				buf.WriteString("\t")
			}
		}
		// Final newline
		buf.WriteString("\n")

		// Write the combined text to the http.ResponseWriter
		w.Write(buf.Bytes())

		return 0 // number of results
	}))

	// Flush the ResponseWriter.
	// Needed in debug mode, where ResponseWriter is buffered.
	L.SetGlobal("flush", L.NewFunction(func(L *lua.LState) int {
		if flushFunc != nil {
			flushFunc()
		}
		return 0 // number of results
	}))

	// Set the Content-Type for the page
	L.SetGlobal("content", L.NewFunction(func(L *lua.LState) int {
		lv := L.ToString(1)
		w.Header().Add("Content-Type", lv)
		return 0 // number of results
	}))

	// Return the current URL Path
	L.SetGlobal("urlpath", L.NewFunction(func(L *lua.LState) int {
		L.Push(lua.LString(req.URL.Path))
		return 1 // number of results
	}))

	// Return the current HTTP method (GET, POST etc)
	L.SetGlobal("method", L.NewFunction(func(L *lua.LState) int {
		L.Push(lua.LString(req.Method))
		return 1 // number of results
	}))

	// Return the HTTP headers as a table
	L.SetGlobal("headers", L.NewFunction(func(L *lua.LState) int {
		luaTable := L.NewTable()
		for key := range req.Header {
			L.RawSet(luaTable, lua.LString(key), lua.LString(req.Header.Get(key)))
		}
		L.Push(luaTable)
		return 1 // number of results
	}))

	// Return the HTTP header in the request, for a given key/string
	L.SetGlobal("header", L.NewFunction(func(L *lua.LState) int {
		key := L.ToString(1)
		value := req.Header.Get(key)
		L.Push(lua.LString(value))
		return 1 // number of results
	}))

	// Set the HTTP header in the request, for a given key and value
	L.SetGlobal("setheader", L.NewFunction(func(L *lua.LState) int {
		key := L.ToString(1)
		value := L.ToString(2)
		w.Header().Set(key, value)
		return 0 // number of results
	}))

	// Return the HTTP body in the request
	L.SetGlobal("body", L.NewFunction(func(L *lua.LState) int {
		body, err := ioutil.ReadAll(req.Body)
		var result lua.LString
		if err != nil {
			result = lua.LString("")
		} else {
			result = lua.LString(string(body))
		}
		L.Push(result)
		return 1 // number of results
	}))

	// Set the HTTP status code (must come before print)
	L.SetGlobal("status", L.NewFunction(func(L *lua.LState) int {
		code := int(L.ToNumber(1))
		w.WriteHeader(code)
		return 0 // number of results
	}))

	// Set a HTTP status code and print a message (optional)
	L.SetGlobal("error", L.NewFunction(func(L *lua.LState) int {
		code := int(L.ToNumber(1))
		w.WriteHeader(code)
		if L.GetTop() == 2 {
			message := L.ToString(2)
			fmt.Fprint(w, message)
		}
		return 0 // number of results
	}))

	// Get the full filename of a given file that is in the directory
	// of the script that is about to be run. If no filename is given,
	// the directory of the script is returned.
	L.SetGlobal("scriptdir", L.NewFunction(func(L *lua.LState) int {
		scriptpath, err := filepath.Abs(filename)
		if err != nil {
			scriptpath = filename
		}
		scriptdir := filepath.Dir(scriptpath)
		scriptpath = scriptdir
		top := L.GetTop()
		if top == 1 {
			// Also include a separator and a filename
			fn := L.ToString(1)
			scriptpath = filepath.Join(scriptdir, fn)
		}
		// Now have the correct absolute scriptpath
		L.Push(lua.LString(scriptpath))
		return 1 // number of results
	}))

	// Given a filename, return the URL path
	L.SetGlobal("file2url", L.NewFunction(func(L *lua.LState) int {
		fn := L.ToString(1)
		targetpath := filepath.Join(filepath.Dir(filename), fn)
		if strings.HasPrefix(targetpath, serverDir) {
			targetpath = targetpath[len(serverDir):]
		}
		if pathsep != "/" {
			// For operating systems that use another path separator for files than for URLs
			targetpath = strings.Replace(targetpath, pathsep, "/", everyInstance)
		}
		withSlashPrefix := path.Join("/", targetpath)
		L.Push(lua.LString(withSlashPrefix))
		return 1 // number of results
	}))

	// Retrieve a table with keys and values from the form in the request
	L.SetGlobal("formdata", L.NewFunction(func(L *lua.LState) int {
		// Place the form data in a map
		m := make(map[string]string)
		req.ParseForm()
		for key, values := range req.Form {
			m[key] = values[0]
		}
		// Convert the map to a table and return it
		L.Push(map2table(L, m))
		return 1 // number of results
	}))

	// Retrieve a table with keys and values from the URL in the request
	L.SetGlobal("urldata", L.NewFunction(func(L *lua.LState) int {

		var valueMap url.Values
		var err error

		if L.GetTop() == 1 {
			// If given an argument
			rawurl := L.ToString(1)
			valueMap, err = url.ParseQuery(rawurl)
			// Log error as warning if there are issues.
			// An empty Value map will then be used.
			if err != nil {
				log.Error(err)
				// return 0
			}
		} else {
			// If not given an argument
			valueMap = req.URL.Query() // map[string][]string
		}

		// Place the Value data in a map, using the first values
		// if there are many values for a given key.
		m := make(map[string]string)
		for key, values := range valueMap {
			m[key] = values[0]
		}
		// Convert the map to a table and return it
		L.Push(map2table(L, m))
		return 1 // number of results
	}))

	// Redirect a request
	L.SetGlobal("redirect", L.NewFunction(func(L *lua.LState) int {
		newurl := L.ToString(1)
		httpStatusCode := http.StatusFound
		if L.GetTop() == 2 {
			if intStatusCode, err := strconv.Atoi(L.ToString(2)); err == nil {
				httpStatusCode = intStatusCode
			}
		}
		http.Redirect(w, req, newurl, httpStatusCode)
		return 0 // number of results
	}))

}
<|endoftext|>


<commit_before>package badge

import (
	"bytes"
	"html/template"
	"io"
	"sync"

	"github.com/golang/freetype/truetype"
	"github.com/narqo/go-badge/fonts"
	"golang.org/x/image/font"
)

type badge struct {
	Subject string
	Status  string
	Color   Color
	Bounds  bounds
}

type bounds struct {
	// SubjectDx is the width of subject string of the badge.
	SubjectDx float64
	SubjectX  float64
	// StatusDx is the width of status string of the badge.
	StatusDx float64
	StatusX  float64
}

func (b bounds) Dx() float64 {
	return b.SubjectDx + b.StatusDx
}

type badgeDrawer struct {
	fd    *font.Drawer
	tmpl  *template.Template
	mutex *sync.Mutex
}

func (d *badgeDrawer) Render(subject, status string, color Color, w io.Writer) error {
	d.mutex.Lock()
	subjectDx := d.measureString(subject)
	statusDx := d.measureString(status)
	d.mutex.Unlock()

	bdg := badge{
		Subject: subject,
		Status:  status,
		Color:   color,
		Bounds: bounds{
			SubjectDx: subjectDx,
			SubjectX:  subjectDx/2.0 + 1,
			StatusDx:  statusDx,
			StatusX:   subjectDx + statusDx/2.0 - 1,
		},
	}
	return d.tmpl.Execute(w, bdg)
}

func (d *badgeDrawer) RenderBytes(subject, status string, color Color) ([]byte, error) {
	d.mutex.Lock()
	subjectDx := d.measureString(subject)
	statusDx := d.measureString(status)
	d.mutex.Unlock()

	bdg := badge{
		Subject: subject,
		Status:  status,
		Color:   color,
		Bounds: bounds{
			SubjectDx: subjectDx,
			SubjectX:  subjectDx/2.0 + 1,
			StatusDx:  statusDx,
			StatusX:   subjectDx + statusDx/2.0 - 1,
		},
	}
	buf := bytes.Buffer{}
	err := d.tmpl.Execute(&buf, bdg)
	return buf.Bytes(), err
}

// shield.io uses Verdana.ttf to measure text width with an extra 10px.
// As we use Vera.ttf, we have to tune this value a little.
const extraDx = 13

func (d *badgeDrawer) measureString(s string) float64 {
	sm := d.fd.MeasureString(s)
	// this 64 is weird but it's the way I've found how to convert fixed.Int26_6 to float64
	return float64(sm)/64 + extraDx
}

// Render renders a badge of the given color, with given subject and status to w.
func Render(subject, status string, color Color, w io.Writer) error {
	return drawer.Render(subject, status, color, w)
}

// RenderBytes renders a badge of the given color, with given subject and status to bytes.
func RenderBytes(subject, status string, color Color) ([]byte, error) {
	return drawer.RenderBytes(subject, status, color)
}

const (
	dpi      = 72
	fontsize = 11
)

var drawer *badgeDrawer

func init() {
	drawer = &badgeDrawer{
		fd:    mustNewFontDrawer(fontsize, dpi),
		tmpl:  template.Must(template.New("flat-template").Parse(flatTemplate)),
		mutex: &sync.Mutex{},
	}
}

func mustNewFontDrawer(size, dpi float64) *font.Drawer {
	ttf, err := truetype.Parse(fonts.VeraSans)
	if err != nil {
		panic(err)
	}
	return &font.Drawer{
		Face: truetype.NewFace(ttf, &truetype.Options{
			Size:    size,
			DPI:     dpi,
			Hinting: font.HintingFull,
		}),
	}
}
<commit_msg>Reuse existing Render method instead of reimplementing it<commit_after>package badge

import (
	"bytes"
	"html/template"
	"io"
	"sync"

	"github.com/golang/freetype/truetype"
	"github.com/narqo/go-badge/fonts"
	"golang.org/x/image/font"
)

type badge struct {
	Subject string
	Status  string
	Color   Color
	Bounds  bounds
}

type bounds struct {
	// SubjectDx is the width of subject string of the badge.
	SubjectDx float64
	SubjectX  float64
	// StatusDx is the width of status string of the badge.
	StatusDx float64
	StatusX  float64
}

func (b bounds) Dx() float64 {
	return b.SubjectDx + b.StatusDx
}

type badgeDrawer struct {
	fd    *font.Drawer
	tmpl  *template.Template
	mutex *sync.Mutex
}

func (d *badgeDrawer) Render(subject, status string, color Color, w io.Writer) error {
	d.mutex.Lock()
	subjectDx := d.measureString(subject)
	statusDx := d.measureString(status)
	d.mutex.Unlock()

	bdg := badge{
		Subject: subject,
		Status:  status,
		Color:   color,
		Bounds: bounds{
			SubjectDx: subjectDx,
			SubjectX:  subjectDx/2.0 + 1,
			StatusDx:  statusDx,
			StatusX:   subjectDx + statusDx/2.0 - 1,
		},
	}
	return d.tmpl.Execute(w, bdg)
}

func (d *badgeDrawer) RenderBytes(subject, status string, color Color) ([]byte, error) {
	buf := &bytes.Buffer{}
	err := d.Render(subject, status, color, buf)
	return buf.Bytes(), err
}

// shield.io uses Verdana.ttf to measure text width with an extra 10px.
// As we use Vera.ttf, we have to tune this value a little.
const extraDx = 13

func (d *badgeDrawer) measureString(s string) float64 {
	sm := d.fd.MeasureString(s)
	// this 64 is weird but it's the way I've found how to convert fixed.Int26_6 to float64
	return float64(sm)/64 + extraDx
}

// Render renders a badge of the given color, with given subject and status to w.
func Render(subject, status string, color Color, w io.Writer) error {
	return drawer.Render(subject, status, color, w)
}

// RenderBytes renders a badge of the given color, with given subject and status to bytes.
func RenderBytes(subject, status string, color Color) ([]byte, error) {
	return drawer.RenderBytes(subject, status, color)
}

const (
	dpi      = 72
	fontsize = 11
)

var drawer *badgeDrawer

func init() {
	drawer = &badgeDrawer{
		fd:    mustNewFontDrawer(fontsize, dpi),
		tmpl:  template.Must(template.New("flat-template").Parse(flatTemplate)),
		mutex: &sync.Mutex{},
	}
}

func mustNewFontDrawer(size, dpi float64) *font.Drawer {
	ttf, err := truetype.Parse(fonts.VeraSans)
	if err != nil {
		panic(err)
	}
	return &font.Drawer{
		Face: truetype.NewFace(ttf, &truetype.Options{
			Size:    size,
			DPI:     dpi,
			Hinting: font.HintingFull,
		}),
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gtime

import (
	"bytes"
	"github.com/gogf/gf/g/text/gregex"
	"github.com/gogf/gf/g/text/gstr"
	"strconv"
	"strings"
)

var (
	// 参考：http://php.net/manual/zh/function.date.php
	formats = map[byte]string{
		// ================== 日 ==================
		'd': "02",     // 月份中的第几天，有前导零的 2 位数字(01 到 31)
		'D': "Mon",    // 星期中的第几天，文本表示，3 个字母(Mon 到 Sun)
		'w': "Monday", // 星期中的第几天，数字型式的文本表示 0为星期天 6为星期六
		'W': "",       // ISO-8601 格式年份中的第几周，每周从星期一开始 例如：42（当年的第 42 周）
		'N': "Monday", // ISO-8601 格式数字表示的星期中的第几天 1（表示星期一）到 7（表示星期天）
		'j': "=j=02",  // 月份中的第几天，没有前导零(1 到 31)
		'S': "02",     // 每月天数后面的英文后缀，2 个字符 st，nd，rd 或者 th。可以和 j 一起用
		'l': "Monday", // ("L"的小写字母)星期几，完整的文本格式(Sunday 到 Saturday)
		'z': "",       // 年份中的第几天  0到365
		't': "",       // 指定的月份有几天 28到31

		// ================== 月 ==================
		'F': "January", // 月份，完整的文本格式，例如 January 或者 March	January 到 December
		'm': "01",      // 数字表示的月份，有前导零(01 到 12)
		'M': "Jan",     // 三个字母缩写表示的月份(Jan 到 Dec)
		'n': "1",       // 数字表示的月份，没有前导零(1 到 12)

		// ================== 年 ==================
		'Y': "2006", // 4 位数字完整表示的年份, 例如：1999 或 2003
		'y': "06",   // 2 位数字表示的年份, 例如：99 或 03

		// ================== 时间 ==================
		'a': "pm",      // 小写的上午和下午值	am 或 pm
		'A': "PM",      // 大写的上午和下午值	AM 或 PM
		'g': "3",       // 小时，12 小时格式，没有前导零,  1 到 12
		'G': "=G=15",   // 小时，24 小时格式，没有前导零,  0 到 23
		'h': "03",      // 小时，12 小时格式，有前导零,   01 到 12
		'H': "15",      // 小时，24 小时格式，有前导零,   00 到 23
		'i': "04",      // 有前导零的分钟数, 00 到 59
		's': "05",      // 秒数，有前导零,   00 到 59
		'u': "=u=.000", // 毫秒(3位)

		// ================== 时区 ==================
		'O': "-0700",  // 与UTC相差的小时数, 例如：+0200
		'P': "-07:00", // 与UTC的差别，小时和分钟之间有冒号分隔, 例如：+02:00
		'T': "MST",    // 时区缩写, 例如：UTC，GMT，CST

		// ================== 完整的日期／时间 ==================
		'c': "2006-01-02T15:04:05-07:00", // ISO 8601 格式的日期，例如：2004-02-12T15:19:21+00:00
		'r': "Mon, 02 Jan 06 15:04 MST",  // RFC 822 格式的日期，例如：Thu, 21 Dec 2000 16:01:07 +0200
	}

	// 星期的英文值和数字值对应map
	weekMap = map[string]string{
		"Sunday":    "0",
		"Monday":    "1",
		"Tuesday":   "2",
		"Wednesday": "3",
		"Thursday":  "4",
		"Friday":    "5",
		"Saturday":  "6",
	}

	// 月份中的第几天数字值和英文值对应map, 最后一个数字为0或大于4时 都为'th'
	monthDayMap = map[string]string{
		"1": "st",
		"2": "nd",
		"3": "rd",
		"4": "th",
	}

	// 每个月累计的天数 不含润年的时候
	dayOfMonth = []int{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}
)

// 将自定义的格式转换为标准库时间格式
func formatToStdLayout(format string) string {
	b := bytes.NewBuffer(nil)
	for i := 0; i < len(format); {
		switch format[i] {
		case '\\':
			if i < len(format)-1 {
				b.WriteByte(format[i+1])
				i += 2
				continue
			} else {
				return b.String()
			}

		default:
			if f, ok := formats[format[i]]; ok {
				// 有几个转换的符号需要特殊处理
				switch format[i] {
				case 'j':
					b.WriteString("02")
				case 'G':
					b.WriteString("15")
				case 'u':
					if i > 0 && format[i-1] == '.' {
						b.WriteString("000")
					} else {
						b.WriteString(".000")
					}

				default:
					b.WriteString(f)
				}
			} else {
				b.WriteByte(format[i])
			}
			i++
		}
	}
	return b.String()
}

// 将format格式转换为正则表达式规则
func formatToRegexPattern(format string) string {
	s := gregex.Quote(formatToStdLayout(format))
	s, _ = gregex.ReplaceString(`[0-9]`, `[0-9]`, s)
	s, _ = gregex.ReplaceString(`[A-Za-z]`, `[A-Za-z]`, s)
	return s
}

// 格式化，使用自定义日期格式
func (t *Time) Format(format string) string {
	runes := []rune(format)
	buffer := bytes.NewBuffer(nil)
	for i := 0; i < len(runes); {
		switch runes[i] {
		case '\\':
			if i < len(runes)-1 {
				buffer.WriteRune(runes[i+1])
				i += 2
				continue
			} else {
				return buffer.String()
			}

		default:
			if runes[i] > 255 {
				buffer.WriteRune(runes[i])
				break
			}
			if f, ok := formats[byte(runes[i])]; ok {
				result := t.Time.Format(f)
				// 有几个转换的符号需要特殊处理
				switch runes[i] {
				case 'j':
					buffer.WriteString(gstr.ReplaceByArray(result, []string{"=j=0", "", "=j=", ""}))
				case 'G':
					buffer.WriteString(gstr.ReplaceByArray(result, []string{"=G=0", "", "=G=", ""}))
				case 'u':
					buffer.WriteString(strings.Replace(result, "=u=.", "", -1))
				case 'w':
					buffer.WriteString(weekMap[result])
				case 'N':
					buffer.WriteString(strings.Replace(weekMap[result], "0", "7", -1))
				case 'S':
					buffer.WriteString(formatMonthDayMap(result[1:]))
				case 'z':
					buffer.WriteString(strconv.Itoa(dayOfYear(t)))
				case 't':
					buffer.WriteString(strconv.Itoa(daysInMonth(t)))
				default:
					buffer.WriteString(result)
				}
			} else {
				buffer.WriteRune(runes[i])
			}
		}
		i++
	}
	return buffer.String()
}

// 每月天数后面的英文后缀，2 个字符st nd，rd 或者 th
func formatMonthDayMap(day string) string {
	if day > "4" || day == "0" {
		return monthDayMap["4"]
	}
	return monthDayMap[day]
}

// 返回是否是润年
func isLeapYear(t *Time) bool {
	year := t.Year()
	if (year%4 == 0 && year%100 != 0) || year%400 == 0 {
		return true
	}
	return false
}

// 返回一个时间点在当年中是第几天 0到365 有润年情况
func dayOfYear(t *Time) int {

	month := int(t.Month())
	day := t.Day()

	// 判断是否润年
	if isLeapYear(t) {
		if month > 2 {
			return dayOfMonth[month-1] + day
		}
		return dayOfMonth[month-1] + day - 1
	}

	return dayOfMonth[month-1] + day - 1

}

// 一个时间点所在的月最长有多条天 28至31
func daysInMonth(t *Time) int {

	month := int(t.Month())
	switch month {
	case 1, 3, 5, 7, 8, 10, 12:
		return 31
	case 4, 6, 9, 11:
		return 30
	}

	// 只剩下第二月份,润年29天
	if isLeapYear(t) {
		return 29
	}

	return 28
}

// 获取时间点在本年内是第多少周
// @todo: 当年的1月1号不为星期一时，那么本年的第一周要从下次的周一开始算。前面的几天算入下一个年的最后周数
func weeksOfYear(t *Time) int {

	days := dayOfYear(t) + 1
	week := int(t.Weekday())
	if week == 0 { //每周从周一开始,星期天是7
		week = 7
	}

	n := (days+7-week)/7 + 1 //这里补齐当前时间周，得到整除，再加上第一周数

	return n

}

//格式化使用标准库格式
func (t *Time) Layout(layout string) string {
	return t.Time.Format(layout)
}
<commit_msg>fmt files<commit_after>// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gtime

import (
	"bytes"
	"github.com/gogf/gf/g/text/gregex"
	"github.com/gogf/gf/g/text/gstr"
	"strconv"
	"strings"
)

var (
	// 参考：http://php.net/manual/zh/function.date.php
	formats = map[byte]string{
		// ================== 日 ==================
		'd': "02",     // 月份中的第几天，有前导零的 2 位数字(01 到 31)
		'D': "Mon",    // 星期中的第几天，文本表示，3 个字母(Mon 到 Sun)
		'w': "Monday", // 星期中的第几天，数字型式的文本表示 0为星期天 6为星期六
		//'W': "",       // ISO-8601 格式年份中的第几周，每周从星期一开始 例如：42（当年的第 42 周）
		'N': "Monday", // ISO-8601 格式数字表示的星期中的第几天 1（表示星期一）到 7（表示星期天）
		'j': "=j=02",  // 月份中的第几天，没有前导零(1 到 31)
		'S': "02",     // 每月天数后面的英文后缀，2 个字符 st，nd，rd 或者 th。可以和 j 一起用
		'l': "Monday", // ("L"的小写字母)星期几，完整的文本格式(Sunday 到 Saturday)
		'z': "",       // 年份中的第几天  0到365
		't': "",       // 指定的月份有几天 28到31

		// ================== 月 ==================
		'F': "January", // 月份，完整的文本格式，例如 January 或者 March	January 到 December
		'm': "01",      // 数字表示的月份，有前导零(01 到 12)
		'M': "Jan",     // 三个字母缩写表示的月份(Jan 到 Dec)
		'n': "1",       // 数字表示的月份，没有前导零(1 到 12)

		// ================== 年 ==================
		'Y': "2006", // 4 位数字完整表示的年份, 例如：1999 或 2003
		'y': "06",   // 2 位数字表示的年份, 例如：99 或 03

		// ================== 时间 ==================
		'a': "pm",      // 小写的上午和下午值	am 或 pm
		'A': "PM",      // 大写的上午和下午值	AM 或 PM
		'g': "3",       // 小时，12 小时格式，没有前导零,  1 到 12
		'G': "=G=15",   // 小时，24 小时格式，没有前导零,  0 到 23
		'h': "03",      // 小时，12 小时格式，有前导零,   01 到 12
		'H': "15",      // 小时，24 小时格式，有前导零,   00 到 23
		'i': "04",      // 有前导零的分钟数, 00 到 59
		's': "05",      // 秒数，有前导零,   00 到 59
		'u': "=u=.000", // 毫秒(3位)

		// ================== 时区 ==================
		'O': "-0700",  // 与UTC相差的小时数, 例如：+0200
		'P': "-07:00", // 与UTC的差别，小时和分钟之间有冒号分隔, 例如：+02:00
		'T': "MST",    // 时区缩写, 例如：UTC，GMT，CST

		// ================== 完整的日期／时间 ==================
		'c': "2006-01-02T15:04:05-07:00", // ISO 8601 格式的日期，例如：2004-02-12T15:19:21+00:00
		'r': "Mon, 02 Jan 06 15:04 MST",  // RFC 822 格式的日期，例如：Thu, 21 Dec 2000 16:01:07 +0200
	}

	// 星期的英文值和数字值对应map
	weekMap = map[string]string{
		"Sunday":    "0",
		"Monday":    "1",
		"Tuesday":   "2",
		"Wednesday": "3",
		"Thursday":  "4",
		"Friday":    "5",
		"Saturday":  "6",
	}

	// 月份中的第几天数字值和英文值对应map, 最后一个数字为0或大于4时 都为'th'
	monthDayMap = map[string]string{
		"1": "st",
		"2": "nd",
		"3": "rd",
		"4": "th",
	}

	// 每个月累计的天数 不含润年的时候
	dayOfMonth = []int{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}
)

// 将自定义的格式转换为标准库时间格式
func formatToStdLayout(format string) string {
	b := bytes.NewBuffer(nil)
	for i := 0; i < len(format); {
		switch format[i] {
		case '\\':
			if i < len(format)-1 {
				b.WriteByte(format[i+1])
				i += 2
				continue
			} else {
				return b.String()
			}

		default:
			if f, ok := formats[format[i]]; ok {
				// 有几个转换的符号需要特殊处理
				switch format[i] {
				case 'j':
					b.WriteString("02")
				case 'G':
					b.WriteString("15")
				case 'u':
					if i > 0 && format[i-1] == '.' {
						b.WriteString("000")
					} else {
						b.WriteString(".000")
					}

				default:
					b.WriteString(f)
				}
			} else {
				b.WriteByte(format[i])
			}
			i++
		}
	}
	return b.String()
}

// 将format格式转换为正则表达式规则
func formatToRegexPattern(format string) string {
	s := gregex.Quote(formatToStdLayout(format))
	s, _ = gregex.ReplaceString(`[0-9]`, `[0-9]`, s)
	s, _ = gregex.ReplaceString(`[A-Za-z]`, `[A-Za-z]`, s)
	return s
}

// 格式化，使用自定义日期格式
func (t *Time) Format(format string) string {
	runes := []rune(format)
	buffer := bytes.NewBuffer(nil)
	for i := 0; i < len(runes); {
		switch runes[i] {
		case '\\':
			if i < len(runes)-1 {
				buffer.WriteRune(runes[i+1])
				i += 2
				continue
			} else {
				return buffer.String()
			}

		default:
			if runes[i] > 255 {
				buffer.WriteRune(runes[i])
				break
			}
			if f, ok := formats[byte(runes[i])]; ok {
				result := t.Time.Format(f)
				// 有几个转换的符号需要特殊处理
				switch runes[i] {
				case 'j':
					buffer.WriteString(gstr.ReplaceByArray(result, []string{"=j=0", "", "=j=", ""}))
				case 'G':
					buffer.WriteString(gstr.ReplaceByArray(result, []string{"=G=0", "", "=G=", ""}))
				case 'u':
					buffer.WriteString(strings.Replace(result, "=u=.", "", -1))
				case 'w':
					buffer.WriteString(weekMap[result])
				case 'N':
					buffer.WriteString(strings.Replace(weekMap[result], "0", "7", -1))
				case 'S':
					buffer.WriteString(formatMonthDayMap(result[1:]))
				case 'z':
					buffer.WriteString(strconv.Itoa(dayOfYear(t)))
				case 't':
					buffer.WriteString(strconv.Itoa(daysInMonth(t)))
				default:
					buffer.WriteString(result)
				}
			} else {
				buffer.WriteRune(runes[i])
			}
		}
		i++
	}
	return buffer.String()
}

// 每月天数后面的英文后缀，2 个字符st nd，rd 或者 th
func formatMonthDayMap(day string) string {
	if day > "4" || day == "0" {
		return monthDayMap["4"]
	}
	return monthDayMap[day]
}

// 返回是否是润年
func isLeapYear(t *Time) bool {
	year := t.Year()
	if (year%4 == 0 && year%100 != 0) || year%400 == 0 {
		return true
	}
	return false
}

// 返回一个时间点在当年中是第几天 0到365 有润年情况
func dayOfYear(t *Time) int {

	month := int(t.Month())
	day := t.Day()

	// 判断是否润年
	if isLeapYear(t) {
		if month > 2 {
			return dayOfMonth[month-1] + day
		}
		return dayOfMonth[month-1] + day - 1
	}
	return dayOfMonth[month-1] + day - 1
}

// 一个时间点所在的月最长有多条天 28至31
func daysInMonth(t *Time) int {

	month := int(t.Month())
	switch month {
	case 1, 3, 5, 7, 8, 10, 12:
		return 31
	case 4, 6, 9, 11:
		return 30
	}

	// 只剩下第二月份,润年29天
	if isLeapYear(t) {
		return 29
	}

	return 28
}

// 获取时间点在本年内是第多少周
// @todo: 当年的1月1号不为星期一时，那么本年的第一周要从下次的周一开始算。前面的几天算入下一个年的最后周数
//func weeksOfYear(t *Time) int {
//
//	days := dayOfYear(t) + 1
//	week := int(t.Weekday())
//	if week == 0 { //每周从周一开始,星期天是7
//		week = 7
//	}
//
//	n := (days+7-week)/7 + 1 //这里补齐当前时间周，得到整除，再加上第一周数
//
//	return n
//
//}

//格式化使用标准库格式
func (t *Time) Layout(layout string) string {
	return t.Time.Format(layout)
}
<|endoftext|>


<commit_before>package main

import (
	"strings"

	"github.com/codegangsta/cli"
	sdk "github.com/dysolution/espsdk"
)

var batchTypes = string(strings.Join(sdk.Batch{}.ValidTypes(), " OR "))

// A Batch wraps the verbs provided by the ESP API for Submission Batches.
type Batch struct{ context *cli.Context }

// Index requests a list of all Submission Batches belonging to the user.
func (b Batch) Index() sdk.BatchListContainer { return sdk.Batch{}.Index(&client) }

// Get requests the metadata for a specific Submission Batch.
func (b Batch) Get() sdk.PrettyPrintable { return sdk.Get(b.path(), &client) }

// Create adds a new Submission Batch.
func (b Batch) Create() sdk.Createable {
	data := b.build()
	return sdk.Create(data.Path(), data, &client)
}

// Update changes fields for an existing Submission Batch.
func (b Batch) Update() sdk.Createable { return batch(b.id()).Update(&client, b.buildUpdate()) }

// Delete destroys a specific Submission Batch.
func (b Batch) Delete() { sdk.Delete(b.path(), &client) }

// Unmarshal attempts to deserialize the provided JSON payload into a SubmissionBatch object.
func (b Batch) Unmarshal(payload []byte) sdk.DeserializedObject {
	return sdk.Unmarshal(payload)
}

func (b Batch) id() int      { return getBatchID(b.context) }
func (b Batch) path() string { return sdk.Batch{ID: b.id()}.Path() }

func (b Batch) build() sdk.Batch {
	return sdk.Batch{
		BriefID:               b.context.String("brief-id"),
		EventID:               b.context.String("event-id"),
		Note:                  b.context.String("note"),
		AssignmentID:          b.context.String("assignment-id"),
		SaveExtractedMetadata: b.context.Bool("save-extracted-metadata"),
		SubmissionName:        b.context.String("submission-name"),
		SubmissionType:        b.context.String("submission-type"),
	}
}

func (b Batch) buildUpdate() sdk.BatchUpdate {
	return sdk.BatchUpdate{b.build()}
}
<commit_msg>accommodate removal of sdk.Batch{}.PrettyPrint()<commit_after>package main

import (
	"strings"

	"github.com/codegangsta/cli"
	sdk "github.com/dysolution/espsdk"
)

var batchTypes = string(strings.Join(sdk.Batch{}.ValidTypes(), " OR "))

// A Batch wraps the verbs provided by the ESP API for Submission Batches.
type Batch struct{ context *cli.Context }

// Index requests a list of all Submission Batches belonging to the user.
func (b Batch) Index() sdk.BatchListContainer { return sdk.Batch{}.Index(&client) }

// Get requests the metadata for a specific Submission Batch.
func (b Batch) Get() sdk.DeserializedObject { return sdk.Get(b.path(), &client) }

// Create adds a new Submission Batch.
func (b Batch) Create() sdk.Createable {
	data := b.build()
	return sdk.Create(data.Path(), data, &client)
}

// Update changes fields for an existing Submission Batch.
func (b Batch) Update() sdk.Createable { return batch(b.id()).Update(&client, b.buildUpdate()) }

// Delete destroys a specific Submission Batch.
func (b Batch) Delete() { sdk.Delete(b.path(), &client) }

// Unmarshal attempts to deserialize the provided JSON payload into a SubmissionBatch object.
func (b Batch) Unmarshal(payload []byte) sdk.DeserializedObject {
	return sdk.Unmarshal(payload)
}

func (b Batch) id() int      { return getBatchID(b.context) }
func (b Batch) path() string { return sdk.Batch{ID: b.id()}.Path() }

func (b Batch) build() sdk.Batch {
	return sdk.Batch{
		BriefID:               b.context.String("brief-id"),
		EventID:               b.context.String("event-id"),
		Note:                  b.context.String("note"),
		AssignmentID:          b.context.String("assignment-id"),
		SaveExtractedMetadata: b.context.Bool("save-extracted-metadata"),
		SubmissionName:        b.context.String("submission-name"),
		SubmissionType:        b.context.String("submission-type"),
	}
}

func (b Batch) buildUpdate() sdk.BatchUpdate {
	return sdk.BatchUpdate{b.build()}
}
<|endoftext|>


<commit_before>package gcs

import (
	"io"

	"cloud.google.com/go/storage"
	"github.com/prometheus/client_golang/prometheus"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"google.golang.org/api/iterator"
	"google.golang.org/api/option"

	"github.com/nyaxt/otaru/blobstore"
	oflags "github.com/nyaxt/otaru/flags"
	gcutil "github.com/nyaxt/otaru/gcloud/util"
	"github.com/nyaxt/otaru/logger"
	oprometheus "github.com/nyaxt/otaru/prometheus"
	"github.com/nyaxt/otaru/util"
)

var mylog = logger.Registry().Category("gcsblobstore")

const promSubsystem = "gcsblobstore"

var issuedOps = prometheus.NewCounterVec(
	prometheus.CounterOpts{
		Name: prometheus.BuildFQName(oprometheus.Namespace, promSubsystem, "issued_ops"),
		Help: "Number of Google Cloud Storage operations issued, partitioned by bucket name and operation type",
	},
	[]string{"optype", "bucketName"},
)
var (
	issuedOpenWriterOps = issuedOps.MustCurryWith(prometheus.Labels{"optype": "openWriter"})
	issuedOpenReaderOps = issuedOps.MustCurryWith(prometheus.Labels{"optype": "openReader"})
	issuedBlobSizeOps   = issuedOps.MustCurryWith(prometheus.Labels{"optype": "blobSize"})
	issuedListBlobOps   = issuedOps.MustCurryWith(prometheus.Labels{"optype": "listBlob"})
	issuedRemoveBlobOps = issuedOps.MustCurryWith(prometheus.Labels{"optype": "RemoveBlob"})
)

func init() {
	prometheus.MustRegister(issuedOps)
}

type GCSBlobStore struct {
	flags      int
	bucket     *storage.BucketHandle
	bucketName string
}

var _ = blobstore.BlobStore(&GCSBlobStore{})

func NewGCSBlobStore(projectName string, bucketName string, tsrc oauth2.TokenSource, flags int) (*GCSBlobStore, error) {
	client, err := storage.NewClient(context.Background(), option.WithTokenSource(tsrc))
	if err != nil {
		return nil, err
	}
	bucket := client.Bucket(bucketName)

	return &GCSBlobStore{
		flags:      flags,
		bucket:     bucket,
		bucketName: bucketName,
	}, nil
}

type Writer struct {
	gcsw *storage.Writer
}

func (bs *GCSBlobStore) OpenWriter(blobpath string) (io.WriteCloser, error) {
	if !oflags.IsWriteAllowed(bs.flags) {
		return nil, util.EACCES
	}

	issuedOpenWriterOps.WithLabelValues(bs.bucketName).Inc()
	logger.Infof(mylog, "OpenWriter(bucketName: %q, %q)", bs.bucketName, blobpath)

	obj := bs.bucket.Object(blobpath)
	gcsw := obj.NewWriter(context.Background())
	gcsw.ContentType = "application/octet-stream"
	return &Writer{gcsw}, nil
}

func (w *Writer) Write(p []byte) (int, error) {
	return w.gcsw.Write(p)
}

func (w *Writer) Close() error {
	if err := w.gcsw.Close(); err != nil {
		return err
	}

	return nil
}

func (bs *GCSBlobStore) tryOpenReaderOnce(blobpath string) (io.ReadCloser, error) {
	issuedOpenReaderOps.WithLabelValues(bs.bucketName).Inc()
	logger.Infof(mylog, "OpenReader(bucketName: %q, %q)", bs.bucketName, blobpath)

	obj := bs.bucket.Object(blobpath)
	rc, err := obj.NewReader(context.Background())
	if err != nil {
		if err == storage.ErrObjectNotExist {
			return nil, util.ENOENT
		}
		return nil, err
	}
	return rc, nil
}

func (bs *GCSBlobStore) OpenReader(blobpath string) (rc io.ReadCloser, err error) {
	gcutil.RetryIfNeeded(func() error {
		rc, err = bs.tryOpenReaderOnce(blobpath)
		return err
	}, mylog)
	return
}

func (bs *GCSBlobStore) Flags() int {
	return bs.flags
}

var _ = blobstore.BlobLister(&GCSBlobStore{})

func (bs *GCSBlobStore) ListBlobs() ([]string, error) {
	issuedListBlobOps.WithLabelValues(bs.bucketName).Inc()
	logger.Infof(mylog, "ListBlobs(bucketName: %q) started.", bs.bucketName)
	defer func() {
		logger.Infof(mylog, "ListBlobs(bucketName: %q) done.", bs.bucketName)
	}()

	ret := make([]string, 0)

	it := bs.bucket.Objects(context.Background(), &storage.Query{})
	for {
		oattr, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return nil, err
		}
		ret = append(ret, oattr.Name)
	}

	return ret, nil
}

var _ = blobstore.BlobSizer(&GCSBlobStore{})

func (bs *GCSBlobStore) BlobSize(blobpath string) (int64, error) {
	issuedBlobSizeOps.WithLabelValues(bs.bucketName).Inc()

	object := bs.bucket.Object(blobpath)
	attrs, err := object.Attrs(context.Background())
	if err != nil {
		if err == storage.ErrObjectNotExist {
			return -1, util.ENOENT
		}
		return -1, err
	}

	logger.Infof(mylog, "BlobSize(bucketName: %q, %q) -> %d", bs.bucketName, blobpath, attrs.Size)
	return attrs.Size, nil
}

var _ = blobstore.BlobRemover(&GCSBlobStore{})

func (bs *GCSBlobStore) RemoveBlob(blobpath string) error {
	if !oflags.IsWriteAllowed(bs.flags) {
		return util.EACCES
	}

	issuedRemoveBlobOps.WithLabelValues(bs.bucketName).Inc()
	logger.Infof(mylog, "RemoveBlob(bucketName: %q, %q)", bs.bucketName, blobpath)

	object := bs.bucket.Object(blobpath)
	if err := object.Delete(context.Background()); err != nil {
		return err
	}
	return nil
}

func (*GCSBlobStore) ImplName() string { return "GCSBlobStore" }
<commit_msg>gcsblobstore: log closes / {read,written}_bytes<commit_after>package gcs

import (
	"io"

	"cloud.google.com/go/storage"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"google.golang.org/api/iterator"
	"google.golang.org/api/option"

	"github.com/nyaxt/otaru/blobstore"
	oflags "github.com/nyaxt/otaru/flags"
	gcutil "github.com/nyaxt/otaru/gcloud/util"
	"github.com/nyaxt/otaru/logger"
	oprometheus "github.com/nyaxt/otaru/prometheus"
	"github.com/nyaxt/otaru/util"
)

var mylog = logger.Registry().Category("gcsblobstore")

const promSubsystem = "gcsblobstore"

var (
	issuedOps = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: prometheus.BuildFQName(oprometheus.Namespace, promSubsystem, "issued_ops"),
			Help: "Number of Google Cloud Storage operations issued, partitioned by bucket name and operation type",
		},
		[]string{"optype", "bucketName"})
	issuedOpenWriterOps  = issuedOps.MustCurryWith(prometheus.Labels{"optype": "openWriter"})
	issuedOpenReaderOps  = issuedOps.MustCurryWith(prometheus.Labels{"optype": "openReader"})
	issuedCloseWriterOps = issuedOps.MustCurryWith(prometheus.Labels{"optype": "closeWriter"})
	issuedCloseReaderOps = issuedOps.MustCurryWith(prometheus.Labels{"optype": "closeReader"})
	issuedBlobSizeOps    = issuedOps.MustCurryWith(prometheus.Labels{"optype": "blobSize"})
	issuedListBlobOps    = issuedOps.MustCurryWith(prometheus.Labels{"optype": "listBlob"})
	issuedRemoveBlobOps  = issuedOps.MustCurryWith(prometheus.Labels{"optype": "RemoveBlob"})

	readBytes = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: prometheus.BuildFQName(oprometheus.Namespace, promSubsystem, "read_bytes"),
			Help: "Number of bytes read from Google Cloud Storage",
		},
		[]string{"bucketName"})
	writtenBytes = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: prometheus.BuildFQName(oprometheus.Namespace, promSubsystem, "written_bytes"),
			Help: "Number of bytes written to Google Cloud Storage",
		},
		[]string{"bucketName"})
)

type GCSBlobStore struct {
	flags      int
	bucket     *storage.BucketHandle
	bucketName string
}

var _ = blobstore.BlobStore(&GCSBlobStore{})

func NewGCSBlobStore(projectName string, bucketName string, tsrc oauth2.TokenSource, flags int) (*GCSBlobStore, error) {
	client, err := storage.NewClient(context.Background(), option.WithTokenSource(tsrc))
	if err != nil {
		return nil, err
	}
	bucket := client.Bucket(bucketName)

	return &GCSBlobStore{
		flags:      flags,
		bucket:     bucket,
		bucketName: bucketName,
	}, nil
}

type Writer struct {
	gcsw       *storage.Writer
	bucketName string
}

func (bs *GCSBlobStore) OpenWriter(blobpath string) (io.WriteCloser, error) {
	if !oflags.IsWriteAllowed(bs.flags) {
		return nil, util.EACCES
	}

	issuedOpenWriterOps.WithLabelValues(bs.bucketName).Inc()
	logger.Infof(mylog, "OpenWriter(bucketName: %q, %q)", bs.bucketName, blobpath)

	obj := bs.bucket.Object(blobpath)
	gcsw := obj.NewWriter(context.Background())
	gcsw.ContentType = "application/octet-stream"
	return &Writer{gcsw, bs.bucketName}, nil
}

func (w *Writer) Write(p []byte) (int, error) {
	writtenBytes.WithLabelValues(w.bucketName).Add(float64(len(p)))
	return w.gcsw.Write(p)
}

func (w *Writer) Close() error {
	issuedCloseWriterOps.WithLabelValues(w.bucketName).Inc()
	if err := w.gcsw.Close(); err != nil {
		return err
	}

	return nil
}

type Reader struct {
	rc         io.ReadCloser
	bucketName string
}

func (bs *GCSBlobStore) tryOpenReaderOnce(blobpath string) (io.ReadCloser, error) {
	issuedOpenReaderOps.WithLabelValues(bs.bucketName).Inc()
	logger.Infof(mylog, "OpenReader(bucketName: %q, %q)", bs.bucketName, blobpath)

	obj := bs.bucket.Object(blobpath)
	rc, err := obj.NewReader(context.Background())
	if err != nil {
		if err == storage.ErrObjectNotExist {
			return nil, util.ENOENT
		}
		return nil, err
	}
	return &Reader{rc, bs.bucketName}, nil
}

func (bs *GCSBlobStore) OpenReader(blobpath string) (rc io.ReadCloser, err error) {
	gcutil.RetryIfNeeded(func() error {
		rc, err = bs.tryOpenReaderOnce(blobpath)
		return err
	}, mylog)
	return
}

func (r *Reader) Read(p []byte) (int, error) {
	readBytes.WithLabelValues(r.bucketName).Add(float64(len(p)))
	return r.rc.Read(p)
}

func (r *Reader) Close() error {
	issuedCloseReaderOps.WithLabelValues(r.bucketName).Inc()
	return r.rc.Close()
}

func (bs *GCSBlobStore) Flags() int {
	return bs.flags
}

var _ = blobstore.BlobLister(&GCSBlobStore{})

func (bs *GCSBlobStore) ListBlobs() ([]string, error) {
	issuedListBlobOps.WithLabelValues(bs.bucketName).Inc()
	logger.Infof(mylog, "ListBlobs(bucketName: %q) started.", bs.bucketName)
	defer func() {
		logger.Infof(mylog, "ListBlobs(bucketName: %q) done.", bs.bucketName)
	}()

	ret := make([]string, 0)

	it := bs.bucket.Objects(context.Background(), &storage.Query{})
	for {
		oattr, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return nil, err
		}
		ret = append(ret, oattr.Name)
	}

	return ret, nil
}

var _ = blobstore.BlobSizer(&GCSBlobStore{})

func (bs *GCSBlobStore) BlobSize(blobpath string) (int64, error) {
	issuedBlobSizeOps.WithLabelValues(bs.bucketName).Inc()

	object := bs.bucket.Object(blobpath)
	attrs, err := object.Attrs(context.Background())
	if err != nil {
		if err == storage.ErrObjectNotExist {
			return -1, util.ENOENT
		}
		return -1, err
	}

	logger.Infof(mylog, "BlobSize(bucketName: %q, %q) -> %d", bs.bucketName, blobpath, attrs.Size)
	return attrs.Size, nil
}

var _ = blobstore.BlobRemover(&GCSBlobStore{})

func (bs *GCSBlobStore) RemoveBlob(blobpath string) error {
	if !oflags.IsWriteAllowed(bs.flags) {
		return util.EACCES
	}

	issuedRemoveBlobOps.WithLabelValues(bs.bucketName).Inc()
	logger.Infof(mylog, "RemoveBlob(bucketName: %q, %q)", bs.bucketName, blobpath)

	object := bs.bucket.Object(blobpath)
	if err := object.Delete(context.Background()); err != nil {
		return err
	}
	return nil
}

func (*GCSBlobStore) ImplName() string { return "GCSBlobStore" }
<|endoftext|>


<commit_before>package router

import (
	"code.google.com/p/gopacket"
	"code.google.com/p/gopacket/layers"
	"syscall"
	"time"
)

type ForwardedFrame struct {
	srcPeer *Peer
	dstPeer *Peer
	frame   []byte
}

type FrameTooBigError struct {
	EPMTU int // effective pmtu, i.e. what we tell packet senders
}

func (conn *LocalConnection) ensureForwarders() error {
	if conn.forwardChan != nil || conn.forwardChanDF != nil {
		return nil
	}
	udpSender := NewSimpleUDPSender(conn)
	udpSenderDF, err := NewRawUDPSender(conn) // only thing that can error, so do it early
	if err != nil {
		return err
	}

	usingPassword := conn.SessionKey != nil
	var encryptor, encryptorDF Encryptor
	if usingPassword {
		encryptor = NewNaClEncryptor(conn.local.NameByte, conn, false)
		encryptorDF = NewNaClEncryptor(conn.local.NameByte, conn, true)
	} else {
		encryptor = NewNonEncryptor(conn.local.NameByte)
		encryptorDF = NewNonEncryptor(conn.local.NameByte)
	}

	var (
		forwardChan   = make(chan *ForwardedFrame, ChannelSize)
		forwardChanDF = make(chan *ForwardedFrame, ChannelSize)
		stopForward   = make(chan interface{}, 0)
		stopForwardDF = make(chan interface{}, 0)
		verifyPMTU    = make(chan int, ChannelSize)
	)
	//NB: only forwarderDF can ever encounter EMSGSIZE errors, and
	//thus perform PMTU verification
	forwarder := NewForwarder(conn, forwardChan, stopForward, nil, encryptor, udpSender, DefaultPMTU)
	forwarderDF := NewForwarder(conn, forwardChanDF, stopForwardDF, verifyPMTU, encryptorDF, udpSenderDF, DefaultPMTU)

	// Various fields in the conn struct are read by other processes,
	// so we have to use locks.
	conn.Lock()
	conn.forwardChan = forwardChan
	conn.forwardChanDF = forwardChanDF
	conn.stopForward = stopForward
	conn.stopForwardDF = stopForwardDF
	conn.verifyPMTU = verifyPMTU
	conn.effectivePMTU = forwarder.unverifiedPMTU
	conn.Unlock()

	forwarder.Start()
	forwarderDF.Start()

	return nil
}

func (conn *LocalConnection) stopForwarders() {
	conn.Lock()
	conn.forwardChan = nil
	conn.forwardChanDF = nil
	conn.Unlock()
	// Now signal the forwarder loops to exit. They will drain the
	// forwarder chans in order to unblock any router processes
	// blocked on sending.
	if conn.stopForward != nil {
		conn.stopForward <- nil
		conn.stopForwardDF <- nil
	}
}

// Called from peer.Relay[Broadcast] which is itself invoked from
// router (both UDP listener process and sniffer process). Also called
// from connection's heartbeat process, and from the connection's TCP
// receiver process.
func (conn *LocalConnection) Forward(df bool, frame *ForwardedFrame, dec *EthernetDecoder) error {
	conn.RLock()
	var (
		forwardChan   = conn.forwardChan
		forwardChanDF = conn.forwardChanDF
		effectivePMTU = conn.effectivePMTU
		stackFrag     = conn.stackFrag
	)
	conn.RUnlock()

	if forwardChan == nil || forwardChanDF == nil {
		conn.Log("Cannot forward frame yet - awaiting contact")
		return nil
	}
	// We could use non-blocking channel sends here, i.e. drop frames
	// on the floor when the forwarder is busy. This would allow our
	// caller - the capturing loop in the router - to read frames more
	// quickly when under load, i.e. we'd drop fewer frames on the
	// floor during capture. And we could maximise CPU utilisation
	// since we aren't stalling a thread. However, a lot of work has
	// already been done by the time we get here. Since any packet we
	// drop will likely get re-transmitted we end up paying that cost
	// multiple times. So it's better to drop things at the beginning
	// of our pipeline.
	if df {
		if !frameTooBig(frame, effectivePMTU) {
			forwardChanDF <- frame
			return nil
		}
		return FrameTooBigError{EPMTU: effectivePMTU}
	}

	if stackFrag || dec == nil || len(dec.decoded) < 2 {
		forwardChan <- frame
		return nil
	}
	// Don't have trustworthy stack, so we're going to have to
	// send it DF in any case.
	if !frameTooBig(frame, effectivePMTU) {
		forwardChanDF <- frame
		return nil
	}
	conn.Router.LogFrame("Fragmenting", frame.frame, &dec.eth)
	// We can't trust the stack to fragment, we have IP, and we
	// have a frame that's too big for the MTU, so we have to
	// fragment it ourself.
	return fragment(dec.eth, dec.ip, effectivePMTU, frame, func(segFrame *ForwardedFrame) {
		forwardChanDF <- segFrame
	})
}

func frameTooBig(frame *ForwardedFrame, effectivePMTU int) bool {
	// We capture/forward complete ethernet frames. Therefore the
	// frame length includes the ethernet header. However, MTUs
	// operate at the IP layer and thus do not include the ethernet
	// header. To put it another way, when a sender that was told an
	// MTU of M sends an IP packet of exactly that length, we will
	// capture/forward M + EthernetOverhead bytes of data.
	return len(frame.frame) > effectivePMTU+EthernetOverhead
}

func fragment(eth layers.Ethernet, ip layers.IPv4, pmtu int, frame *ForwardedFrame, forward func(*ForwardedFrame)) error {
	// We are not doing any sort of NAT, so we don't need to worry
	// about checksums of IP payload (eg UDP checksum).
	headerSize := int(ip.IHL) * 4
	// &^ is bit clear (AND NOT). So here we're clearing the lowest 3
	// bits.
	maxSegmentSize := (pmtu - headerSize) &^ 7
	opts := gopacket.SerializeOptions{
		FixLengths:       false,
		ComputeChecksums: true}
	payloadSize := int(ip.Length) - headerSize
	payload := ip.BaseLayer.Payload[:payloadSize]
	offsetBase := int(ip.FragOffset) << 3
	origFlags := ip.Flags
	ip.Flags = ip.Flags | layers.IPv4MoreFragments
	ip.Length = uint16(headerSize + maxSegmentSize)
	if eth.EthernetType == layers.EthernetTypeLLC {
		// using LLC, so must set eth length correctly. eth length
		// is just the length of the payload
		eth.Length = ip.Length
	} else {
		eth.Length = 0
	}
	for offset := 0; offset < payloadSize; offset += maxSegmentSize {
		var segmentPayload []byte
		if len(payload) <= maxSegmentSize {
			// last one
			segmentPayload = payload
			ip.Length = uint16(len(payload) + headerSize)
			ip.Flags = origFlags
			if eth.EthernetType == layers.EthernetTypeLLC {
				eth.Length = ip.Length
			} else {
				eth.Length = 0
			}
		} else {
			segmentPayload = payload[:maxSegmentSize]
			payload = payload[maxSegmentSize:]
		}
		ip.FragOffset = uint16((offset + offsetBase) >> 3)
		buf := gopacket.NewSerializeBuffer()
		segPayload := gopacket.Payload(segmentPayload)
		err := gopacket.SerializeLayers(buf, opts, &eth, &ip, &segPayload)
		if err != nil {
			return err
		}
		// make copies of the frame we received
		segFrame := *frame
		segFrame.frame = buf.Bytes()
		forward(&segFrame)
	}
	return nil
}

// Forwarder

type Forwarder struct {
	conn            *LocalConnection
	ch              <-chan *ForwardedFrame
	stop            <-chan interface{}
	verifyPMTUTick  <-chan time.Time
	verifyPMTU      <-chan int
	pmtuVerifyCount uint
	enc             Encryptor
	udpSender       UDPSender
	maxPayload      int
	pmtuVerified    bool
	highestGoodPMTU int
	unverifiedPMTU  int
	lowestBadPMTU   int
}

func NewForwarder(conn *LocalConnection, ch <-chan *ForwardedFrame, stop <-chan interface{}, verifyPMTU <-chan int, enc Encryptor, udpSender UDPSender, pmtu int) *Forwarder {
	fwd := &Forwarder{
		conn:       conn,
		ch:         ch,
		stop:       stop,
		verifyPMTU: verifyPMTU,
		enc:        enc,
		udpSender:  udpSender}
	fwd.unverifiedPMTU = pmtu - fwd.effectiveOverhead()
	fwd.maxPayload = pmtu - UDPOverhead
	return fwd
}

func (fwd *Forwarder) Start() {
	go fwd.run()
}

func (fwd *Forwarder) run() {
	defer fwd.udpSender.Shutdown()
	for {
		select {
		case <-fwd.stop:
			fwd.drain()
			return
		case <-fwd.verifyPMTUTick:
			// We only do this case here when we know the buffers are
			// all empty so that we don't risk appending verify-frames
			// to other data.
			fwd.verifyPMTUTick = nil
			if fwd.pmtuVerified {
				continue
			}
			if fwd.pmtuVerifyCount > 0 {
				fwd.pmtuVerifyCount--
				fwd.attemptVerifyEffectivePMTU()
			} else {
				// we've exceeded the verification attempts of the
				// unverifiedPMTU
				fwd.lowestBadPMTU = fwd.unverifiedPMTU
				fwd.verifyEffectivePMTU((fwd.highestGoodPMTU + fwd.lowestBadPMTU) / 2)
			}
		case epmtu := <-fwd.verifyPMTU:
			if fwd.pmtuVerified || epmtu != fwd.unverifiedPMTU {
				continue
			}
			if epmtu+1 < fwd.lowestBadPMTU {
				fwd.highestGoodPMTU = fwd.unverifiedPMTU // = epmtu
				fwd.verifyEffectivePMTU((fwd.highestGoodPMTU + fwd.lowestBadPMTU) / 2)
			} else {
				fwd.pmtuVerified = true
				fwd.maxPayload = epmtu + fwd.effectiveOverhead() - UDPOverhead
				fwd.conn.setEffectivePMTU(epmtu)
				fwd.conn.Log("Effective PMTU verified at", epmtu)
			}
		case frame := <-fwd.ch:
			if !fwd.appendFrame(frame) {
				fwd.logDrop(frame)
				continue
			}
			fwd.accumulateAndSendFrames()
		}
	}
}

func (fwd *Forwarder) effectiveOverhead() int {
	return UDPOverhead + fwd.enc.PacketOverhead() + fwd.enc.FrameOverhead() + EthernetOverhead
}

func (fwd *Forwarder) verifyEffectivePMTU(newUnverifiedPMTU int) {
	fwd.unverifiedPMTU = newUnverifiedPMTU
	fwd.pmtuVerifyCount = PMTUVerifyAttempts
	fwd.attemptVerifyEffectivePMTU()
}

func (fwd *Forwarder) attemptVerifyEffectivePMTU() {
	pmtuVerifyFrame := &ForwardedFrame{
		srcPeer: fwd.conn.local,
		dstPeer: fwd.conn.remote,
		frame:   make([]byte, fwd.unverifiedPMTU+EthernetOverhead)}
	fwd.enc.AppendFrame(pmtuVerifyFrame)
	fwd.flush()
	if fwd.verifyPMTUTick == nil {
		fwd.verifyPMTUTick = time.After(PMTUVerifyTimeout << (PMTUVerifyAttempts - fwd.pmtuVerifyCount))
	}
}

func (fwd *Forwarder) accumulateAndSendFrames() {
	for {
		select {
		case frame := <-fwd.ch:
			if !fwd.appendFrame(frame) {
				fwd.flush()
				if !fwd.appendFrame(frame) {
					fwd.logDrop(frame)
					return
				}
			}
		default:
			fwd.flush()
			return
		}
	}
}

func (fwd *Forwarder) appendFrame(frame *ForwardedFrame) bool {
	frameLen := len(frame.frame)
	if fwd.enc.TotalLen()+fwd.enc.FrameOverhead()+frameLen > fwd.maxPayload {
		return false
	}
	fwd.enc.AppendFrame(frame)
	return true
}

func (fwd *Forwarder) flush() {
	err := fwd.udpSender.Send(fwd.enc.Bytes())
	if err != nil {
		if mtbe, ok := err.(MsgTooBigError); ok {
			newUnverifiedPMTU := mtbe.PMTU - fwd.effectiveOverhead()
			if newUnverifiedPMTU >= fwd.unverifiedPMTU {
				return
			}
			fwd.pmtuVerified = false
			fwd.maxPayload = mtbe.PMTU - UDPOverhead
			fwd.highestGoodPMTU = 8
			fwd.lowestBadPMTU = newUnverifiedPMTU + 1
			fwd.conn.setEffectivePMTU(newUnverifiedPMTU)
			fwd.verifyEffectivePMTU(newUnverifiedPMTU)
		} else if PosixError(err) == syscall.ENOBUFS {
			// TODO handle this better
		} else {
			fwd.conn.Shutdown(err)
		}
	}
}

func (fwd *Forwarder) drain() {
	// We want to drain before exiting otherwise we could get the
	// packet sniffer or udp listener blocked on sending to a full
	// chan
	for {
		select {
		case <-fwd.ch:
		default:
			return
		}
	}
}

func (fwd *Forwarder) logDrop(frame *ForwardedFrame) {
	fwd.conn.Log("Dropping too big frame during forwarding: frame len:", len(frame.frame), "; effective PMTU:", fwd.maxPayload+UDPOverhead-fwd.effectiveOverhead())
}
<commit_msg>keep accumulating frames even when dropping some This logically makes more sense, i.e. we keep draining frames from fwd.ch until we run out.<commit_after>package router

import (
	"code.google.com/p/gopacket"
	"code.google.com/p/gopacket/layers"
	"syscall"
	"time"
)

type ForwardedFrame struct {
	srcPeer *Peer
	dstPeer *Peer
	frame   []byte
}

type FrameTooBigError struct {
	EPMTU int // effective pmtu, i.e. what we tell packet senders
}

func (conn *LocalConnection) ensureForwarders() error {
	if conn.forwardChan != nil || conn.forwardChanDF != nil {
		return nil
	}
	udpSender := NewSimpleUDPSender(conn)
	udpSenderDF, err := NewRawUDPSender(conn) // only thing that can error, so do it early
	if err != nil {
		return err
	}

	usingPassword := conn.SessionKey != nil
	var encryptor, encryptorDF Encryptor
	if usingPassword {
		encryptor = NewNaClEncryptor(conn.local.NameByte, conn, false)
		encryptorDF = NewNaClEncryptor(conn.local.NameByte, conn, true)
	} else {
		encryptor = NewNonEncryptor(conn.local.NameByte)
		encryptorDF = NewNonEncryptor(conn.local.NameByte)
	}

	var (
		forwardChan   = make(chan *ForwardedFrame, ChannelSize)
		forwardChanDF = make(chan *ForwardedFrame, ChannelSize)
		stopForward   = make(chan interface{}, 0)
		stopForwardDF = make(chan interface{}, 0)
		verifyPMTU    = make(chan int, ChannelSize)
	)
	//NB: only forwarderDF can ever encounter EMSGSIZE errors, and
	//thus perform PMTU verification
	forwarder := NewForwarder(conn, forwardChan, stopForward, nil, encryptor, udpSender, DefaultPMTU)
	forwarderDF := NewForwarder(conn, forwardChanDF, stopForwardDF, verifyPMTU, encryptorDF, udpSenderDF, DefaultPMTU)

	// Various fields in the conn struct are read by other processes,
	// so we have to use locks.
	conn.Lock()
	conn.forwardChan = forwardChan
	conn.forwardChanDF = forwardChanDF
	conn.stopForward = stopForward
	conn.stopForwardDF = stopForwardDF
	conn.verifyPMTU = verifyPMTU
	conn.effectivePMTU = forwarder.unverifiedPMTU
	conn.Unlock()

	forwarder.Start()
	forwarderDF.Start()

	return nil
}

func (conn *LocalConnection) stopForwarders() {
	conn.Lock()
	conn.forwardChan = nil
	conn.forwardChanDF = nil
	conn.Unlock()
	// Now signal the forwarder loops to exit. They will drain the
	// forwarder chans in order to unblock any router processes
	// blocked on sending.
	if conn.stopForward != nil {
		conn.stopForward <- nil
		conn.stopForwardDF <- nil
	}
}

// Called from peer.Relay[Broadcast] which is itself invoked from
// router (both UDP listener process and sniffer process). Also called
// from connection's heartbeat process, and from the connection's TCP
// receiver process.
func (conn *LocalConnection) Forward(df bool, frame *ForwardedFrame, dec *EthernetDecoder) error {
	conn.RLock()
	var (
		forwardChan   = conn.forwardChan
		forwardChanDF = conn.forwardChanDF
		effectivePMTU = conn.effectivePMTU
		stackFrag     = conn.stackFrag
	)
	conn.RUnlock()

	if forwardChan == nil || forwardChanDF == nil {
		conn.Log("Cannot forward frame yet - awaiting contact")
		return nil
	}
	// We could use non-blocking channel sends here, i.e. drop frames
	// on the floor when the forwarder is busy. This would allow our
	// caller - the capturing loop in the router - to read frames more
	// quickly when under load, i.e. we'd drop fewer frames on the
	// floor during capture. And we could maximise CPU utilisation
	// since we aren't stalling a thread. However, a lot of work has
	// already been done by the time we get here. Since any packet we
	// drop will likely get re-transmitted we end up paying that cost
	// multiple times. So it's better to drop things at the beginning
	// of our pipeline.
	if df {
		if !frameTooBig(frame, effectivePMTU) {
			forwardChanDF <- frame
			return nil
		}
		return FrameTooBigError{EPMTU: effectivePMTU}
	}

	if stackFrag || dec == nil || len(dec.decoded) < 2 {
		forwardChan <- frame
		return nil
	}
	// Don't have trustworthy stack, so we're going to have to
	// send it DF in any case.
	if !frameTooBig(frame, effectivePMTU) {
		forwardChanDF <- frame
		return nil
	}
	conn.Router.LogFrame("Fragmenting", frame.frame, &dec.eth)
	// We can't trust the stack to fragment, we have IP, and we
	// have a frame that's too big for the MTU, so we have to
	// fragment it ourself.
	return fragment(dec.eth, dec.ip, effectivePMTU, frame, func(segFrame *ForwardedFrame) {
		forwardChanDF <- segFrame
	})
}

func frameTooBig(frame *ForwardedFrame, effectivePMTU int) bool {
	// We capture/forward complete ethernet frames. Therefore the
	// frame length includes the ethernet header. However, MTUs
	// operate at the IP layer and thus do not include the ethernet
	// header. To put it another way, when a sender that was told an
	// MTU of M sends an IP packet of exactly that length, we will
	// capture/forward M + EthernetOverhead bytes of data.
	return len(frame.frame) > effectivePMTU+EthernetOverhead
}

func fragment(eth layers.Ethernet, ip layers.IPv4, pmtu int, frame *ForwardedFrame, forward func(*ForwardedFrame)) error {
	// We are not doing any sort of NAT, so we don't need to worry
	// about checksums of IP payload (eg UDP checksum).
	headerSize := int(ip.IHL) * 4
	// &^ is bit clear (AND NOT). So here we're clearing the lowest 3
	// bits.
	maxSegmentSize := (pmtu - headerSize) &^ 7
	opts := gopacket.SerializeOptions{
		FixLengths:       false,
		ComputeChecksums: true}
	payloadSize := int(ip.Length) - headerSize
	payload := ip.BaseLayer.Payload[:payloadSize]
	offsetBase := int(ip.FragOffset) << 3
	origFlags := ip.Flags
	ip.Flags = ip.Flags | layers.IPv4MoreFragments
	ip.Length = uint16(headerSize + maxSegmentSize)
	if eth.EthernetType == layers.EthernetTypeLLC {
		// using LLC, so must set eth length correctly. eth length
		// is just the length of the payload
		eth.Length = ip.Length
	} else {
		eth.Length = 0
	}
	for offset := 0; offset < payloadSize; offset += maxSegmentSize {
		var segmentPayload []byte
		if len(payload) <= maxSegmentSize {
			// last one
			segmentPayload = payload
			ip.Length = uint16(len(payload) + headerSize)
			ip.Flags = origFlags
			if eth.EthernetType == layers.EthernetTypeLLC {
				eth.Length = ip.Length
			} else {
				eth.Length = 0
			}
		} else {
			segmentPayload = payload[:maxSegmentSize]
			payload = payload[maxSegmentSize:]
		}
		ip.FragOffset = uint16((offset + offsetBase) >> 3)
		buf := gopacket.NewSerializeBuffer()
		segPayload := gopacket.Payload(segmentPayload)
		err := gopacket.SerializeLayers(buf, opts, &eth, &ip, &segPayload)
		if err != nil {
			return err
		}
		// make copies of the frame we received
		segFrame := *frame
		segFrame.frame = buf.Bytes()
		forward(&segFrame)
	}
	return nil
}

// Forwarder

type Forwarder struct {
	conn            *LocalConnection
	ch              <-chan *ForwardedFrame
	stop            <-chan interface{}
	verifyPMTUTick  <-chan time.Time
	verifyPMTU      <-chan int
	pmtuVerifyCount uint
	enc             Encryptor
	udpSender       UDPSender
	maxPayload      int
	pmtuVerified    bool
	highestGoodPMTU int
	unverifiedPMTU  int
	lowestBadPMTU   int
}

func NewForwarder(conn *LocalConnection, ch <-chan *ForwardedFrame, stop <-chan interface{}, verifyPMTU <-chan int, enc Encryptor, udpSender UDPSender, pmtu int) *Forwarder {
	fwd := &Forwarder{
		conn:       conn,
		ch:         ch,
		stop:       stop,
		verifyPMTU: verifyPMTU,
		enc:        enc,
		udpSender:  udpSender}
	fwd.unverifiedPMTU = pmtu - fwd.effectiveOverhead()
	fwd.maxPayload = pmtu - UDPOverhead
	return fwd
}

func (fwd *Forwarder) Start() {
	go fwd.run()
}

func (fwd *Forwarder) run() {
	defer fwd.udpSender.Shutdown()
	for {
		select {
		case <-fwd.stop:
			fwd.drain()
			return
		case <-fwd.verifyPMTUTick:
			// We only do this case here when we know the buffers are
			// all empty so that we don't risk appending verify-frames
			// to other data.
			fwd.verifyPMTUTick = nil
			if fwd.pmtuVerified {
				continue
			}
			if fwd.pmtuVerifyCount > 0 {
				fwd.pmtuVerifyCount--
				fwd.attemptVerifyEffectivePMTU()
			} else {
				// we've exceeded the verification attempts of the
				// unverifiedPMTU
				fwd.lowestBadPMTU = fwd.unverifiedPMTU
				fwd.verifyEffectivePMTU((fwd.highestGoodPMTU + fwd.lowestBadPMTU) / 2)
			}
		case epmtu := <-fwd.verifyPMTU:
			if fwd.pmtuVerified || epmtu != fwd.unverifiedPMTU {
				continue
			}
			if epmtu+1 < fwd.lowestBadPMTU {
				fwd.highestGoodPMTU = fwd.unverifiedPMTU // = epmtu
				fwd.verifyEffectivePMTU((fwd.highestGoodPMTU + fwd.lowestBadPMTU) / 2)
			} else {
				fwd.pmtuVerified = true
				fwd.maxPayload = epmtu + fwd.effectiveOverhead() - UDPOverhead
				fwd.conn.setEffectivePMTU(epmtu)
				fwd.conn.Log("Effective PMTU verified at", epmtu)
			}
		case frame := <-fwd.ch:
			if !fwd.appendFrame(frame) {
				fwd.logDrop(frame)
				continue
			}
			fwd.accumulateAndSendFrames()
		}
	}
}

func (fwd *Forwarder) effectiveOverhead() int {
	return UDPOverhead + fwd.enc.PacketOverhead() + fwd.enc.FrameOverhead() + EthernetOverhead
}

func (fwd *Forwarder) verifyEffectivePMTU(newUnverifiedPMTU int) {
	fwd.unverifiedPMTU = newUnverifiedPMTU
	fwd.pmtuVerifyCount = PMTUVerifyAttempts
	fwd.attemptVerifyEffectivePMTU()
}

func (fwd *Forwarder) attemptVerifyEffectivePMTU() {
	pmtuVerifyFrame := &ForwardedFrame{
		srcPeer: fwd.conn.local,
		dstPeer: fwd.conn.remote,
		frame:   make([]byte, fwd.unverifiedPMTU+EthernetOverhead)}
	fwd.enc.AppendFrame(pmtuVerifyFrame)
	fwd.flush()
	if fwd.verifyPMTUTick == nil {
		fwd.verifyPMTUTick = time.After(PMTUVerifyTimeout << (PMTUVerifyAttempts - fwd.pmtuVerifyCount))
	}
}

func (fwd *Forwarder) accumulateAndSendFrames() {
	for {
		select {
		case frame := <-fwd.ch:
			if !fwd.appendFrame(frame) {
				fwd.flush()
				if !fwd.appendFrame(frame) {
					fwd.logDrop(frame)
				}
			}
		default:
			fwd.flush()
			return
		}
	}
}

func (fwd *Forwarder) appendFrame(frame *ForwardedFrame) bool {
	frameLen := len(frame.frame)
	if fwd.enc.TotalLen()+fwd.enc.FrameOverhead()+frameLen > fwd.maxPayload {
		return false
	}
	fwd.enc.AppendFrame(frame)
	return true
}

func (fwd *Forwarder) flush() {
	err := fwd.udpSender.Send(fwd.enc.Bytes())
	if err != nil {
		if mtbe, ok := err.(MsgTooBigError); ok {
			newUnverifiedPMTU := mtbe.PMTU - fwd.effectiveOverhead()
			if newUnverifiedPMTU >= fwd.unverifiedPMTU {
				return
			}
			fwd.pmtuVerified = false
			fwd.maxPayload = mtbe.PMTU - UDPOverhead
			fwd.highestGoodPMTU = 8
			fwd.lowestBadPMTU = newUnverifiedPMTU + 1
			fwd.conn.setEffectivePMTU(newUnverifiedPMTU)
			fwd.verifyEffectivePMTU(newUnverifiedPMTU)
		} else if PosixError(err) == syscall.ENOBUFS {
			// TODO handle this better
		} else {
			fwd.conn.Shutdown(err)
		}
	}
}

func (fwd *Forwarder) drain() {
	// We want to drain before exiting otherwise we could get the
	// packet sniffer or udp listener blocked on sending to a full
	// chan
	for {
		select {
		case <-fwd.ch:
		default:
			return
		}
	}
}

func (fwd *Forwarder) logDrop(frame *ForwardedFrame) {
	fwd.conn.Log("Dropping too big frame during forwarding: frame len:", len(frame.frame), "; effective PMTU:", fwd.maxPayload+UDPOverhead-fwd.effectiveOverhead())
}
<|endoftext|>


<commit_before>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package api

import (
	"fmt"
	"github.com/globocom/tsuru/auth"
	"github.com/globocom/tsuru/errors"
	"github.com/globocom/tsuru/log"
	"net/http"
)

const (
	tsuruMin = "0.7.11"
	craneMin = "0.4"
)

func setVersionHeaders(w http.ResponseWriter) {
	w.Header().Set("Supported-Tsuru", tsuruMin)
	w.Header().Set("Supported-Crane", craneMin)
}

type handler func(http.ResponseWriter, *http.Request) error

func (fn handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	setVersionHeaders(w)
	defer func() {
		if r.Body != nil {
			r.Body.Close()
		}
	}()
	fw := FlushingWriter{w, false}
	if err := fn(&fw, r); err != nil {
		if fw.wrote {
			fmt.Fprintln(&fw, err)
		} else {
			http.Error(&fw, err.Error(), http.StatusInternalServerError)
		}
		log.Print(err)
	}
}

func validate(token string, r *http.Request) (*auth.Token, error) {
	if token == "" {
		return nil, &errors.Http{
			Message: "You must provide the Authorization header",
		}
	}
	invalid := &errors.Http{Message: "Invalid token"}
	t, err := auth.GetToken(token)
	if err != nil {
		return nil, invalid
	}
	if t.AppName != "" {
		if q := r.URL.Query().Get(":app"); q != "" && t.AppName != q {
			return nil, invalid
		}
	}
	return t, nil
}

type authorizationRequiredHandler func(http.ResponseWriter, *http.Request, *auth.Token) error

func (fn authorizationRequiredHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	setVersionHeaders(w)
	defer func() {
		if r.Body != nil {
			r.Body.Close()
		}
	}()
	fw := FlushingWriter{w, false}
	token := r.Header.Get("Authorization")
	if t, err := validate(token, r); err != nil {
		http.Error(&fw, err.Error(), http.StatusUnauthorized)
	} else if err = fn(&fw, r, t); err != nil {
		code := http.StatusInternalServerError
		if e, ok := err.(*errors.Http); ok {
			code = e.Code
		}
		if fw.wrote {
			fmt.Fprintln(&fw, err)
		} else {
			http.Error(&fw, err.Error(), code)
		}
		log.Print(err)
	}
}

type adminRequiredHandler authorizationRequiredHandler

func (fn adminRequiredHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	setVersionHeaders(w)
	defer func() {
		if r.Body != nil {
			r.Body.Close()
		}
	}()
	fw := FlushingWriter{w, false}
	header := r.Header.Get("Authorization")
	if header == "" {
		http.Error(&fw, "You must provide the Authorization header", http.StatusUnauthorized)
	} else if t, err := auth.GetToken(header); err != nil {
		http.Error(&fw, "Invalid token", http.StatusUnauthorized)
	} else if user, err := t.User(); err != nil || !user.IsAdmin() {
		http.Error(&fw, "Forbidden", http.StatusForbidden)
	} else if err = fn(&fw, r, t); err != nil {
		code := http.StatusInternalServerError
		if e, ok := err.(*errors.Http); ok {
			code = e.Code
		}
		if fw.wrote {
			fmt.Fprintln(&fw, err)
		} else {
			http.Error(&fw, err.Error(), code)
		}
		log.Print(err)
	}
}
<commit_msg>api: require crane 0.4.4<commit_after>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package api

import (
	"fmt"
	"github.com/globocom/tsuru/auth"
	"github.com/globocom/tsuru/errors"
	"github.com/globocom/tsuru/log"
	"net/http"
)

const (
	tsuruMin = "0.7.11"
	craneMin = "0.4.4"
)

func setVersionHeaders(w http.ResponseWriter) {
	w.Header().Set("Supported-Tsuru", tsuruMin)
	w.Header().Set("Supported-Crane", craneMin)
}

type handler func(http.ResponseWriter, *http.Request) error

func (fn handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	setVersionHeaders(w)
	defer func() {
		if r.Body != nil {
			r.Body.Close()
		}
	}()
	fw := FlushingWriter{w, false}
	if err := fn(&fw, r); err != nil {
		if fw.wrote {
			fmt.Fprintln(&fw, err)
		} else {
			http.Error(&fw, err.Error(), http.StatusInternalServerError)
		}
		log.Print(err)
	}
}

func validate(token string, r *http.Request) (*auth.Token, error) {
	if token == "" {
		return nil, &errors.Http{
			Message: "You must provide the Authorization header",
		}
	}
	invalid := &errors.Http{Message: "Invalid token"}
	t, err := auth.GetToken(token)
	if err != nil {
		return nil, invalid
	}
	if t.AppName != "" {
		if q := r.URL.Query().Get(":app"); q != "" && t.AppName != q {
			return nil, invalid
		}
	}
	return t, nil
}

type authorizationRequiredHandler func(http.ResponseWriter, *http.Request, *auth.Token) error

func (fn authorizationRequiredHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	setVersionHeaders(w)
	defer func() {
		if r.Body != nil {
			r.Body.Close()
		}
	}()
	fw := FlushingWriter{w, false}
	token := r.Header.Get("Authorization")
	if t, err := validate(token, r); err != nil {
		http.Error(&fw, err.Error(), http.StatusUnauthorized)
	} else if err = fn(&fw, r, t); err != nil {
		code := http.StatusInternalServerError
		if e, ok := err.(*errors.Http); ok {
			code = e.Code
		}
		if fw.wrote {
			fmt.Fprintln(&fw, err)
		} else {
			http.Error(&fw, err.Error(), code)
		}
		log.Print(err)
	}
}

type adminRequiredHandler authorizationRequiredHandler

func (fn adminRequiredHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	setVersionHeaders(w)
	defer func() {
		if r.Body != nil {
			r.Body.Close()
		}
	}()
	fw := FlushingWriter{w, false}
	header := r.Header.Get("Authorization")
	if header == "" {
		http.Error(&fw, "You must provide the Authorization header", http.StatusUnauthorized)
	} else if t, err := auth.GetToken(header); err != nil {
		http.Error(&fw, "Invalid token", http.StatusUnauthorized)
	} else if user, err := t.User(); err != nil || !user.IsAdmin() {
		http.Error(&fw, "Forbidden", http.StatusForbidden)
	} else if err = fn(&fw, r, t); err != nil {
		code := http.StatusInternalServerError
		if e, ok := err.(*errors.Http); ok {
			code = e.Code
		}
		if fw.wrote {
			fmt.Fprintln(&fw, err)
		} else {
			http.Error(&fw, err.Error(), code)
		}
		log.Print(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"fmt"
	"time"

	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
	"github.com/sagikazarmark/healthz"
	"github.com/sagikazarmark/serverz"
)

func bootstrap() serverz.Server {
	serviceChecker := healthz.NewTCPChecker(config.ServiceAddr, healthz.WithTCPTimeout(2*time.Second))
	checkerCollector.RegisterChecker(healthz.LivenessCheck, serviceChecker)

	db, err := sql.Open(
		"mysql",
		fmt.Sprintf(
			"%s:%s@tcp(%s:%d)/%s?parseTime=true",
			config.DbUser,
			config.DbPass,
			config.DbHost,
			config.DbPort,
			config.DbName,
		),
	)

	if err != nil {
		logger.Panic(err)
	}

	checkerCollector.RegisterChecker(healthz.ReadinessCheck, healthz.NewPingChecker(db))

	server := createGrpcServer()

	// Register servers here

	grpc_prometheus.Register(server)

	return &serverz.NamedServer{
		Server: &serverz.GrpcServer{server},
		Name:   "grpc",
	}
}
<commit_msg>Add missing database import<commit_after>package main

import (
	"database/sql"
	"fmt"
	"time"

	_ "github.com/go-sql-driver/mysql"
	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
	"github.com/sagikazarmark/healthz"
	"github.com/sagikazarmark/serverz"
)

func bootstrap() serverz.Server {
	serviceChecker := healthz.NewTCPChecker(config.ServiceAddr, healthz.WithTCPTimeout(2*time.Second))
	checkerCollector.RegisterChecker(healthz.LivenessCheck, serviceChecker)

	db, err := sql.Open(
		"mysql",
		fmt.Sprintf(
			"%s:%s@tcp(%s:%d)/%s?parseTime=true",
			config.DbUser,
			config.DbPass,
			config.DbHost,
			config.DbPort,
			config.DbName,
		),
	)

	if err != nil {
		logger.Panic(err)
	}

	checkerCollector.RegisterChecker(healthz.ReadinessCheck, healthz.NewPingChecker(db))

	server := createGrpcServer()

	// Register servers here

	grpc_prometheus.Register(server)

	return &serverz.NamedServer{
		Server: &serverz.GrpcServer{server},
		Name:   "grpc",
	}
}
<|endoftext|>


<commit_before>//Package bayes implements a naive Bayes classifier.  Trained models are
//stored in a SQLite database.
package bayes

/*Takes a text sample (stemmed state doesn't matter and is only used for
 *improving accuracy) and either trains the system based on it, or classifies
 *it based on previously trained data.*/

import (
	"database/sql"
	_ "github.com/mattn/go-sqlite3"
	"log"
	"sync"
)

//Tolerance := 0.05 //Tolerance -- i.e. if the result is 0.5 +- tolerance value, return "undetermined" category

type index struct {
	sync.RWMutex
	m map[string]float64
}

//Classify takes a text sample and classifies it as one of two categories.
func Classify(text []string, cat1Index string, cat2Index string, tolerance float64) int {
	myCat1Index := loadIndex(cat1Index)
	myCat2Index := loadIndex(cat2Index)
	myCat1Index.RLock()
	myCat2Index.RLock()
	//iterate over goodindex and badindex, and sum the values.
	cat1total := 0.00
	cat2total := 0.00
	for _, value := range myCat1Index.m {
		cat1total += value
	}
	for _, value := range myCat2Index.m {
		cat2total += value
	}
	i := 0.00
	invi := 0.00
	for _, token := range text {
		cat1token := myCat1Index.m[token]
		cat2token := myCat2Index.m[token]
		prob := calcProbability(cat1token, cat1total, cat2token, cat2total)
		if i != 0.00 {
			i = i * prob
		} else {
			i = prob
		}
		if invi != 0.00 {
			invi = invi * (1.00 - prob)
		} else {
			invi = 1.00 - prob
		}
	}
	myCat1Index.RUnlock()
	myCat2Index.RUnlock()
	totprob := i / (i + invi)
	if totprob > (0.5 + tolerance) {
		return 1
	} else if totprob < (0.5 - tolerance) {
		return -1
	}
	return 0
}

//calcProbability calculates the probability any given word is part of either category
func calcProbability(cat1count float64, cat1total float64, cat2count float64, cat2total float64) float64 {
	bw := cat1count / cat1total
	gw := cat2count / cat2total
	pw := ((bw) / ((bw) + (gw)))
	s := 1.00
	x := 0.5
	n := cat1count + cat2count
	fw := ((s * x) + (n * pw)) / (s + n)
	return fw

}

//Train trains a category with a text sample.
func Train(trainedIndex string, text []string) {
	myIndex := loadIndex(trainedIndex)
	myIndex.Lock()
	for _, token := range text {
		myIndex.m[token]++
	}
	saveIndex(trainedIndex, myIndex)
	myIndex.Unlock()
}

//loadIndex loads an index from a database.
func loadIndex(indexName string) index {

	myIndex := index{}
	database, err := sql.Open("sqlite3", "./bayes.db")
	_, err = database.Exec(
		"CREATE TABLE IF NOT EXISTS Bayes ( id varchar(255), token varchar(255), count REAL, PRIMARY KEY (id, token))")
	if err != nil {
		log.Fatal(err)
	}
	sth, err := database.Query("SELECT token, count FROM Bayes WHERE id=?", indexName)
	myIndex.m = make(map[string]float64)
	for sth.Next() {
		var token sql.NullString
		var count sql.NullFloat64
		if err := sth.Scan(&token, &count); err != nil {
			log.Fatal(err)
		}
		myCount := count.Float64
		myToken := token.String
		myIndex.m[myToken] = myCount
	}
	database.Close()
	return myIndex
	//load index from database
}

//saveIndex saves the index to the database.
func saveIndex(indexName string, myIndex index) {
	//save index to database
	database, err := sql.Open("sqlite3", "./rss2.db")
	if err != nil {
		log.Fatal(err)
	}
	for token, count := range myIndex.m {
		database.Exec("INSERT OR REPLACE INTO Bayes(id, token, count) VALUES(?,?,?)", indexName, token, count)
	}
	database.Close()
}
<commit_msg>Update bayes.go<commit_after>//Package bayes implements a naive Bayes classifier.  Trained models are
//stored in a SQLite database.
package bayes

/*Takes a text sample (stemmed state doesn't matter and is only used for
 *improving accuracy) and either trains the system based on it, or classifies
 *it based on previously trained data.*/

import (
	"sync"
	"math"
)

type Index struct {
	sync.RWMutex
	M map[string]float64
	Name string
	Total float64
}

//Classify takes a text sample and classifies it as one of two categories.
func Classify(text []string, cat1Index Index, cat2Index Index, tolerance float64) string {
	cat1Index.RLock()
	cat2Index.RLock()
	i := 0.00
	invi := 0.00
	for _, token := range text {
		prob := calcProbability(cat1Index.M[token], cat1Index.Total, cat2Index.M[token], cat2Index.Total)
		if i > 0.00  && prob > 0.00{
			i = i * prob
		} else if i == 0.00 {
			i = prob
		}
		if invi != 0.00 {
			invi = invi * (1.00 - prob)
		} else {
			invi = 1.00 - prob
		}
	}
	cat1Index.RUnlock()
	cat2Index.RUnlock()
	totprob := i / (i + invi)
	if totprob > (0.5 + tolerance) {
		return cat1Index.Name
	} else if totprob < (0.5 - tolerance) {
		return cat2Index.Name
	}
	return "Unknown"
}

//calcProbability calculates the probability any given word is part of either category
func calcProbability(cat1count float64, cat1total float64, cat2count float64, cat2total float64) float64 {
	cat1Percent:= math.Log(cat1count / cat1total)
	cat2Percent:= math.Log(cat2count / cat2total)
	probWord := cat1Percent / (cat1Percent + cat2Percent)
	if math.IsNaN(probWord) { //Word doesn't exist in either of the indices
		probWord = 0.0
	}
	return probWord
}

//Train trains a category with a text sample.
func Train(myIndex Index, text []string) Index {
	//myIndex := loadIndex(trainedIndex)
	myIndex.Lock()
	for _, token := range text {
		myIndex.M[token]++
		myIndex.Total++
	}
	myIndex.Unlock()
	return myIndex
}
<|endoftext|>