<commit_before>package air

import (
	"bytes"
	"errors"
	"fmt"
	"net/http"
	"path"
	"reflect"
	"runtime"
	"sync"
)

type (
	// Air is the top-level framework struct.
	Air struct {
		pregases    []Gas
		gases       []Gas
		paramCap    int
		contextPool *sync.Pool
		server      *server
		router      *router

		Config           *Config
		Logger           Logger
		Binder           Binder
		Minifier         Minifier
		Renderer         Renderer
		Coffer           Coffer
		HTTPErrorHandler HTTPErrorHandler
	}

	// Handler defines a function to serve HTTP requests.
	Handler func(*Context) error

	// Gas defines a function to process gases.
	Gas func(Handler) Handler

	// HTTPError represents an error that occurred while handling an HTTP request.
	HTTPError struct {
		Code    int
		Message string
	}

	// HTTPErrorHandler is a centralized HTTP error handler.
	HTTPErrorHandler func(error, *Context)

	// Map is the `map[string]interface{}`.
	Map map[string]interface{}
)

// HTTP methods
const (
	GET    = "GET"
	POST   = "POST"
	PUT    = "PUT"
	DELETE = "DELETE"
)

// For easy for-range
var methods = [4]string{GET, POST, PUT, DELETE}

// MIME types
const (
	MIMEApplicationJSON               = "application/json"
	MIMEApplicationJavaScript         = "application/javascript"
	MIMEApplicationMSGPack            = "application/msgpack"
	MIMEApplicationOctetStream        = "application/octet-stream"
	MIMEApplicationProtoBuf           = "application/protobuf"
	MIMEApplicationXML                = "application/xml"
	MIMEApplicationXWWWFormURLEncoded = "application/x-www-form-urlencoded"
	MIMEApplicationXYAML              = "application/x-yaml"
	MIMEImageSVGXML                   = "image/svg+xml"
	MIMEMultipartFormData             = "multipart/form-data"
	MIMETextCSS                       = "text/css"
	MIMETextHTML                      = "text/html"
	MIMETextJavaScript                = "text/javascript"
	MIMETextPlain                     = "text/plain"
	MIMETextXML                       = "text/xml"

	CharsetUTF8 = "; charset=utf-8"
)

// Headers
const (
	HeaderAcceptEncoding                = "Accept-Encoding"
	HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials"
	HeaderAccessControlAllowHeaders     = "Access-Control-Allow-Headers"
	HeaderAccessControlAllowMethods     = "Access-Control-Allow-Methods"
	HeaderAccessControlAllowOrigin      = "Access-Control-Allow-Origin"
	HeaderAccessControlExposeHeaders    = "Access-Control-Expose-Headers"
	HeaderAccessControlMaxAge           = "Access-Control-Max-Age"
	HeaderAccessControlRequestHeaders   = "Access-Control-Request-Headers"
	HeaderAccessControlRequestMethod    = "Access-Control-Request-Method"
	HeaderAllow                         = "Allow"
	HeaderAuthorization                 = "Authorization"
	HeaderContentDisposition            = "Content-Disposition"
	HeaderContentEncoding               = "Content-Encoding"
	HeaderContentLength                 = "Content-Length"
	HeaderContentSecurityPolicy         = "Content-Security-Policy"
	HeaderContentType                   = "Content-Type"
	HeaderCookie                        = "Cookie"
	HeaderIfModifiedSince               = "If-Modified-Since"
	HeaderLastModified                  = "Last-Modified"
	HeaderLocation                      = "Location"
	HeaderOrigin                        = "Origin"
	HeaderServer                        = "Server"
	HeaderSetCookie                     = "Set-Cookie"
	HeaderStrictTransportSecurity       = "Strict-Transport-Security"
	HeaderUpgrade                       = "Upgrade"
	HeaderVary                          = "Vary"
	HeaderWWWAuthenticate               = "WWW-Authenticate"
	HeaderXCSRFToken                    = "X-CSRF-Token"
	HeaderXContentTypeOptions           = "X-Content-Type-Options"
	HeaderXForwardedFor                 = "X-Forwarded-For"
	HeaderXForwardedProto               = "X-Forwarded-Proto"
	HeaderXFrameOptions                 = "X-Frame-Options"
	HeaderXHTTPMethodOverride           = "X-HTTP-Method-Override"
	HeaderXRealIP                       = "X-Real-IP"
	HeaderXXSSProtection                = "X-XSS-Protection"
)

// Errors
var (
	ErrUnauthorized                = NewHTTPError(http.StatusUnauthorized)          // 401
	ErrNotFound                    = NewHTTPError(http.StatusNotFound)              // 404
	ErrMethodNotAllowed            = NewHTTPError(http.StatusMethodNotAllowed)      // 405
	ErrStatusRequestEntityTooLarge = NewHTTPError(http.StatusRequestEntityTooLarge) // 413
	ErrUnsupportedMediaType        = NewHTTPError(http.StatusUnsupportedMediaType)  // 415

	ErrInternalServerError = NewHTTPError(http.StatusInternalServerError) // 500
	ErrBadGateway          = NewHTTPError(http.StatusBadGateway)          // 502
	ErrServiceUnavailable  = NewHTTPError(http.StatusServiceUnavailable)  // 503
	ErrGatewayTimeout      = NewHTTPError(http.StatusGatewayTimeout)      // 504

	ErrInvalidRedirectCode = errors.New("invalid redirect status code")
)

// HTTP error handlers
var (
	NotFoundHandler = func(c *Context) error {
		return ErrNotFound
	}

	MethodNotAllowedHandler = func(c *Context) error {
		return ErrMethodNotAllowed
	}
)

// New returns a pointer of a new instance of the `Air`.
func New() *Air {
	a := &Air{}

	a.contextPool = &sync.Pool{
		New: func() interface{} {
			return NewContext(a)
		},
	}
	a.server = newServer(a)
	a.router = newRouter(a)

	a.Config = NewConfig("config.yml")
	a.Logger = newLogger(a)
	a.Binder = newBinder()
	a.Minifier = newMinifier()
	a.Renderer = newRenderer(a)
	a.Coffer = newCoffer(a)
	a.HTTPErrorHandler = DefaultHTTPErrorHandler

	return a
}

// Precontain adds the gases to the chain which is perform before the router.
func (a *Air) Precontain(gases ...Gas) {
	a.pregases = append(a.pregases, gases...)
}

// Contain adds the gases to the chain which is perform after the router.
func (a *Air) Contain(gases ...Gas) {
	a.gases = append(a.gases, gases...)
}

// GET registers a new GET route for the path with the matching h in the router with the optional
// route-level gases.
func (a *Air) GET(path string, h Handler, gases ...Gas) {
	a.add(GET, path, h, gases...)
}

// POST registers a new POST route for the path with the matching h in the router with the optional
// route-level gases.
func (a *Air) POST(path string, h Handler, gases ...Gas) {
	a.add(POST, path, h, gases...)
}

// PUT registers a new PUT route for the path with the matching h in the router with the optional
// route-level gases.
func (a *Air) PUT(path string, h Handler, gases ...Gas) {
	a.add(PUT, path, h, gases...)
}

// DELETE registers a new DELETE route for the path with the matching h in the router with the
// optional route-level gases.
func (a *Air) DELETE(path string, h Handler, gases ...Gas) {
	a.add(DELETE, path, h, gases...)
}

// Static registers a new route with the path prefix to serve the static files from the provided
// root directory.
func (a *Air) Static(prefix, root string) {
	a.GET(prefix+"*", func(c *Context) error {
		return c.File(path.Join(root, c.Param("*")))
	})
}

// File registers a new route with the path to serve a static file.
func (a *Air) File(path, file string) {
	a.GET(path, func(c *Context) error {
		return c.File(file)
	})
}

// add registers a new route for the path with the method and the matching h in the router with the
// optional route-level gases.
func (a *Air) add(method, path string, h Handler, gases ...Gas) {
	hn := handlerName(h)

	a.router.add(method, path, func(c *Context) error {
		h := h
		for i := len(gases) - 1; i >= 0; i-- {
			h = gases[i](h)
		}
		return h(c)
	})

	a.router.routes[method+path] = &route{
		method:  method,
		path:    path,
		handler: hn,
	}
}

// URL returns an URL generated from the h with the optional params.
func (a *Air) URL(h Handler, params ...interface{}) string {
	url := &bytes.Buffer{}
	hn := handlerName(h)
	ln := len(params)
	n := 0

	for _, r := range a.router.routes {
		if r.handler == hn {
			for i, l := 0, len(r.path); i < l; i++ {
				if r.path[i] == ':' && n < ln {
					for ; i < l && r.path[i] != '/'; i++ {
					}
					url.WriteString(fmt.Sprintf("%v", params[n]))
					n++
				}

				if i < l {
					url.WriteByte(r.path[i])
				}
			}

			break
		}
	}

	return url.String()
}

// Serve starts the HTTP server.
func (a *Air) Serve() error {
	if a.Config.DebugMode {
		a.Config.LoggerEnabled = true
		a.Logger.Debug("serving in debug mode")
	}

	go func() {
		if err := a.Minifier.Init(); err != nil {
			a.Logger.Error(err)
		}

		if err := a.Renderer.Init(); err != nil {
			a.Logger.Error(err)
		}

		if err := a.Coffer.Init(); err != nil {
			a.Logger.Error(err)
		}
	}()

	return a.server.serve()
}

// Close closes the HTTP server immediately.
func (a *Air) Close() error {
	return a.server.Close()
}

// Shutdown gracefully shuts down the HTTP server without interrupting any active connections.
func (a *Air) Shutdown(c *Context) error {
	return a.server.Shutdown(c.Context)
}

// handlerName returns the func name of the h.
func handlerName(h Handler) string {
	return runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name()
}

// WrapHandler wraps the h into the `Handler`.
func WrapHandler(h http.Handler) Handler {
	return func(c *Context) error {
		h.ServeHTTP(c.Response, c.Request.Request)
		return nil
	}
}

// WrapGas wraps the h into the `Gas`.
func WrapGas(h Handler) Gas {
	return func(next Handler) Handler {
		return func(c *Context) error {
			if err := h(c); err != nil {
				return err
			}
			return next(c)
		}
	}
}

// NewHTTPError returns a pointer of a new instance of the `HTTPError`.
func NewHTTPError(code int, messages ...interface{}) *HTTPError {
	he := &HTTPError{Code: code, Message: http.StatusText(code)}
	if len(messages) > 0 {
		he.Message = fmt.Sprint(messages...)
	}
	return he
}

// Error implements the `error#Error()`.
func (he *HTTPError) Error() string {
	return he.Message
}

// DefaultHTTPErrorHandler is the default HTTP error handler.
func DefaultHTTPErrorHandler(err error, c *Context) {
	he := ErrInternalServerError

	if che, ok := err.(*HTTPError); ok {
		he = che
	}

	if c.Air.Config.DebugMode {
		he.Message = err.Error()
	}

	if !c.Response.Written() {
		c.Response.WriteHeader(he.Code)
		c.String(he.Message)
	}

	c.Air.Logger.Error(err)
}
<commit_msg>refactor: enrich HTTP headers<commit_after>package air

import (
	"bytes"
	"errors"
	"fmt"
	"net/http"
	"path"
	"reflect"
	"runtime"
	"sync"
)

type (
	// Air is the top-level framework struct.
	Air struct {
		pregases    []Gas
		gases       []Gas
		paramCap    int
		contextPool *sync.Pool
		server      *server
		router      *router

		Config           *Config
		Logger           Logger
		Binder           Binder
		Minifier         Minifier
		Renderer         Renderer
		Coffer           Coffer
		HTTPErrorHandler HTTPErrorHandler
	}

	// Handler defines a function to serve HTTP requests.
	Handler func(*Context) error

	// Gas defines a function to process gases.
	Gas func(Handler) Handler

	// HTTPError represents an error that occurred while handling an HTTP request.
	HTTPError struct {
		Code    int
		Message string
	}

	// HTTPErrorHandler is a centralized HTTP error handler.
	HTTPErrorHandler func(error, *Context)

	// Map is the `map[string]interface{}`.
	Map map[string]interface{}
)

// HTTP methods
const (
	GET    = "GET"
	POST   = "POST"
	PUT    = "PUT"
	DELETE = "DELETE"
)

// For easy for-range
var methods = [4]string{GET, POST, PUT, DELETE}

// MIME types
const (
	MIMEApplicationJSON               = "application/json"
	MIMEApplicationJavaScript         = "application/javascript"
	MIMEApplicationMSGPack            = "application/msgpack"
	MIMEApplicationOctetStream        = "application/octet-stream"
	MIMEApplicationProtoBuf           = "application/protobuf"
	MIMEApplicationXML                = "application/xml"
	MIMEApplicationXWWWFormURLEncoded = "application/x-www-form-urlencoded"
	MIMEApplicationXYAML              = "application/x-yaml"
	MIMEImageSVGXML                   = "image/svg+xml"
	MIMEMultipartFormData             = "multipart/form-data"
	MIMETextCSS                       = "text/css"
	MIMETextHTML                      = "text/html"
	MIMETextJavaScript                = "text/javascript"
	MIMETextPlain                     = "text/plain"
	MIMETextXML                       = "text/xml"

	CharsetUTF8 = "; charset=utf-8"
)

// HTTP Headers
const (
	HeaderAccept                          = "Accept"
	HeaderAcceptCharset                   = "Accept-Charset"
	HeaderAcceptEncoding                  = "Accept-Encoding"
	HeaderAcceptLanguage                  = "Accept-Language"
	HeaderAcceptRanges                    = "Accept-Ranges"
	HeaderAccessControlAllowCredentials   = "Access-Control-Allow-Credentials"
	HeaderAccessControlAllowHeaders       = "Access-Control-Allow-Headers"
	HeaderAccessControlAllowMethods       = "Access-Control-Allow-Methods"
	HeaderAccessControlAllowOrigin        = "Access-Control-Allow-Origin"
	HeaderAccessControlExposeHeaders      = "Access-Control-Expose-Headers"
	HeaderAccessControlMaxAge             = "Access-Control-Max-Age"
	HeaderAccessControlRequestHeaders     = "Access-Control-Request-Headers"
	HeaderAccessControlRequestMethod      = "Access-Control-Request-Method"
	HeaderAge                             = "Age"
	HeaderAllow                           = "Allow"
	HeaderAuthorization                   = "Authorization"
	HeaderCacheControl                    = "Cache-Control"
	HeaderConnection                      = "Connection"
	HeaderContentDisposition              = "Content-Disposition"
	HeaderContentEncoding                 = "Content-Encoding"
	HeaderContentLanguage                 = "Content-Language"
	HeaderContentLength                   = "Content-Length"
	HeaderContentLocation                 = "Content-Location"
	HeaderContentSecurityPolicy           = "Content-Security-Policy"
	HeaderContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only"
	HeaderContentType                     = "Content-Type"
	HeaderCookie                          = "Cookie"
	HeaderDNT                             = "DNT"
	HeaderDate                            = "Date"
	HeaderETag                            = "ETag"
	HeaderExpires                         = "Expires"
	HeaderForm                            = "Form"
	HeaderHost                            = "Host"
	HeaderIfMatch                         = "If-Match"
	HeaderIfModifiedSince                 = "If-Modified-Since"
	HeaderIfNoneMatch                     = "If-None-Match"
	HeaderIfRange                         = "If-Range"
	HeaderIfUnmodifiedSince               = "If-Unmodified-Since"
	HeaderKeepAlive                       = "Keep-Alive"
	HeaderLastModified                    = "Last-Modified"
	HeaderLocation                        = "Location"
	HeaderOrigin                          = "Origin"
	HeaderPublicKeyPins                   = "Public-Key-Pins"
	HeaderPublicKeyPinsReportOnly         = "Public-Key-Pins-Report-Only"
	HeaderReferer                         = "Referer"
	HeaderReferrerPolicy                  = "Referrer-Policy"
	HeaderRetryAfter                      = "Retry-After"
	HeaderServer                          = "Server"
	HeaderSetCookie                       = "Set-Cookie"
	HeaderStrictTransportSecurity         = "Strict-Transport-Security"
	HeaderTE                              = "TE"
	HeaderTK                              = "TK"
	HeaderTrailer                         = "Trailer"
	HeaderTransferEncoding                = "Transfer-Encoding"
	HeaderUpgrade                         = "Upgrade"
	HeaderUpgradeInsecureRequests         = "Upgrade-Insecure-Requests"
	HeaderUserAgent                       = "User-Agent"
	HeaderVary                            = "Vary"
	HeaderVia                             = "Via"
	HeaderWWWAuthenticate                 = "WWW-Authenticate"
	HeaderWarning                         = "Warning"
	HeaderXCSRFToken                      = "X-CSRF-Token"
	HeaderXContentTypeOptions             = "X-Content-Type-Options"
	HeaderXDNSPrefetchControl             = "X-DNS-Prefetch-Control"
	HeaderXForwardedFor                   = "X-Forwarded-For"
	HeaderXForwardedProto                 = "X-Forwarded-Proto"
	HeaderXFrameOptions                   = "X-Frame-Options"
	HeaderXHTTPMethodOverride             = "X-HTTP-Method-Override"
	HeaderXRealIP                         = "X-Real-IP"
	HeaderXXSSProtection                  = "X-XSS-Protection"
)

// HTTP Errors
var (
	ErrUnauthorized                = NewHTTPError(http.StatusUnauthorized)          // 401
	ErrNotFound                    = NewHTTPError(http.StatusNotFound)              // 404
	ErrMethodNotAllowed            = NewHTTPError(http.StatusMethodNotAllowed)      // 405
	ErrStatusRequestEntityTooLarge = NewHTTPError(http.StatusRequestEntityTooLarge) // 413
	ErrUnsupportedMediaType        = NewHTTPError(http.StatusUnsupportedMediaType)  // 415

	ErrInternalServerError = NewHTTPError(http.StatusInternalServerError) // 500
	ErrBadGateway          = NewHTTPError(http.StatusBadGateway)          // 502
	ErrServiceUnavailable  = NewHTTPError(http.StatusServiceUnavailable)  // 503
	ErrGatewayTimeout      = NewHTTPError(http.StatusGatewayTimeout)      // 504

	ErrInvalidRedirectCode = errors.New("invalid redirect status code")
)

// HTTP error handlers
var (
	NotFoundHandler = func(c *Context) error {
		return ErrNotFound
	}

	MethodNotAllowedHandler = func(c *Context) error {
		return ErrMethodNotAllowed
	}
)

// New returns a pointer of a new instance of the `Air`.
func New() *Air {
	a := &Air{}

	a.contextPool = &sync.Pool{
		New: func() interface{} {
			return NewContext(a)
		},
	}
	a.server = newServer(a)
	a.router = newRouter(a)

	a.Config = NewConfig("config.yml")
	a.Logger = newLogger(a)
	a.Binder = newBinder()
	a.Minifier = newMinifier()
	a.Renderer = newRenderer(a)
	a.Coffer = newCoffer(a)
	a.HTTPErrorHandler = DefaultHTTPErrorHandler

	return a
}

// Precontain adds the gases to the chain which is perform before the router.
func (a *Air) Precontain(gases ...Gas) {
	a.pregases = append(a.pregases, gases...)
}

// Contain adds the gases to the chain which is perform after the router.
func (a *Air) Contain(gases ...Gas) {
	a.gases = append(a.gases, gases...)
}

// GET registers a new GET route for the path with the matching h in the router with the optional
// route-level gases.
func (a *Air) GET(path string, h Handler, gases ...Gas) {
	a.add(GET, path, h, gases...)
}

// POST registers a new POST route for the path with the matching h in the router with the optional
// route-level gases.
func (a *Air) POST(path string, h Handler, gases ...Gas) {
	a.add(POST, path, h, gases...)
}

// PUT registers a new PUT route for the path with the matching h in the router with the optional
// route-level gases.
func (a *Air) PUT(path string, h Handler, gases ...Gas) {
	a.add(PUT, path, h, gases...)
}

// DELETE registers a new DELETE route for the path with the matching h in the router with the
// optional route-level gases.
func (a *Air) DELETE(path string, h Handler, gases ...Gas) {
	a.add(DELETE, path, h, gases...)
}

// Static registers a new route with the path prefix to serve the static files from the provided
// root directory.
func (a *Air) Static(prefix, root string) {
	a.GET(prefix+"*", func(c *Context) error {
		return c.File(path.Join(root, c.Param("*")))
	})
}

// File registers a new route with the path to serve a static file.
func (a *Air) File(path, file string) {
	a.GET(path, func(c *Context) error {
		return c.File(file)
	})
}

// add registers a new route for the path with the method and the matching h in the router with the
// optional route-level gases.
func (a *Air) add(method, path string, h Handler, gases ...Gas) {
	hn := handlerName(h)

	a.router.add(method, path, func(c *Context) error {
		h := h
		for i := len(gases) - 1; i >= 0; i-- {
			h = gases[i](h)
		}
		return h(c)
	})

	a.router.routes[method+path] = &route{
		method:  method,
		path:    path,
		handler: hn,
	}
}

// URL returns an URL generated from the h with the optional params.
func (a *Air) URL(h Handler, params ...interface{}) string {
	url := &bytes.Buffer{}
	hn := handlerName(h)
	ln := len(params)
	n := 0

	for _, r := range a.router.routes {
		if r.handler == hn {
			for i, l := 0, len(r.path); i < l; i++ {
				if r.path[i] == ':' && n < ln {
					for ; i < l && r.path[i] != '/'; i++ {
					}
					url.WriteString(fmt.Sprintf("%v", params[n]))
					n++
				}

				if i < l {
					url.WriteByte(r.path[i])
				}
			}

			break
		}
	}

	return url.String()
}

// Serve starts the HTTP server.
func (a *Air) Serve() error {
	if a.Config.DebugMode {
		a.Config.LoggerEnabled = true
		a.Logger.Debug("serving in debug mode")
	}

	go func() {
		if err := a.Minifier.Init(); err != nil {
			a.Logger.Error(err)
		}

		if err := a.Renderer.Init(); err != nil {
			a.Logger.Error(err)
		}

		if err := a.Coffer.Init(); err != nil {
			a.Logger.Error(err)
		}
	}()

	return a.server.serve()
}

// Close closes the HTTP server immediately.
func (a *Air) Close() error {
	return a.server.Close()
}

// Shutdown gracefully shuts down the HTTP server without interrupting any active connections.
func (a *Air) Shutdown(c *Context) error {
	return a.server.Shutdown(c.Context)
}

// handlerName returns the func name of the h.
func handlerName(h Handler) string {
	return runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name()
}

// WrapHandler wraps the h into the `Handler`.
func WrapHandler(h http.Handler) Handler {
	return func(c *Context) error {
		h.ServeHTTP(c.Response, c.Request.Request)
		return nil
	}
}

// WrapGas wraps the h into the `Gas`.
func WrapGas(h Handler) Gas {
	return func(next Handler) Handler {
		return func(c *Context) error {
			if err := h(c); err != nil {
				return err
			}
			return next(c)
		}
	}
}

// NewHTTPError returns a pointer of a new instance of the `HTTPError`.
func NewHTTPError(code int, messages ...interface{}) *HTTPError {
	he := &HTTPError{Code: code, Message: http.StatusText(code)}
	if len(messages) > 0 {
		he.Message = fmt.Sprint(messages...)
	}
	return he
}

// Error implements the `error#Error()`.
func (he *HTTPError) Error() string {
	return he.Message
}

// DefaultHTTPErrorHandler is the default HTTP error handler.
func DefaultHTTPErrorHandler(err error, c *Context) {
	he := ErrInternalServerError

	if che, ok := err.(*HTTPError); ok {
		he = che
	}

	if c.Air.Config.DebugMode {
		he.Message = err.Error()
	}

	if !c.Response.Written() {
		c.Response.WriteHeader(he.Code)
		c.String(he.Message)
	}

	c.Air.Logger.Error(err)
}
<|endoftext|>


<commit_before>package globals

var Version = "1.0.0"
<commit_msg>chore: bump the backage version manually before release<commit_after>package globals

var Version = "2.0.0"
<|endoftext|>


<commit_before>package wspacego

import (
	. "github.com/r7kamura/gospel"
	"path"
	"runtime"
	"testing"
)

func TestInterpretor(t *testing.T) {
	Describe(t, "インタープリターのテスト", func() {
		data := []byte{'A', ' ', 'B', '\t', '\r', '\n'}
		Context("インスタンス生成", func() {
			It("インスタンスが生成できること", func() {
				actual := NewInterpreter(data)
				Expect(actual).To(Exist)
				Expect(actual.origin).To(Equal, data)
			})
		})
		Context("不要な文字を排除する関数", func() {
			It("不要なデータ以外排除されていること", func() {
				sut := NewInterpreter(data)
				sut.filter()
				Expect(sut.source).To(Equal, []byte{' ', '\t', '\n'})
			})
			It("不要なデータ以外排除されていること（改行を増やす）", func() {
				data = append(data, '\n')
				sut := NewInterpreter(data)
				sut.filter()
				Expect(sut.source).To(Equal, []byte{' ', '\t', '\n', '\n'})
			})
		})
		Context("ソースファイルをコマンドリストに変換する関数", func() {
			It("スタックに１をプッシュするコマンドが作成されること", func() {
				data = []byte{'P', 'u', 's', 'h', ' ', ' ', '\t', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewSubCommandWithParam("stack", "push", 1))
			})
		})
	})
}

func current_dir() string {
	_, fpath, _, _ := runtime.Caller(0)
	return path.Dir(fpath)
}
<commit_msg>スタックをコピーする命令を実装<commit_after>package wspacego

import (
	. "github.com/r7kamura/gospel"
	"path"
	"runtime"
	"testing"
)

func TestInterpretor(t *testing.T) {
	Describe(t, "インタープリターのテスト", func() {
		data := []byte{'A', ' ', 'B', '\t', '\r', '\n'}
		Context("インスタンス生成", func() {
			It("インスタンスが生成できること", func() {
				actual := NewInterpreter(data)
				Expect(actual).To(Exist)
				Expect(actual.origin).To(Equal, data)
			})
		})
		Context("不要な文字を排除する関数", func() {
			It("不要なデータ以外排除されていること", func() {
				sut := NewInterpreter(data)
				sut.filter()
				Expect(sut.source).To(Equal, []byte{' ', '\t', '\n'})
			})
			It("不要なデータ以外排除されていること（改行を増やす）", func() {
				data = append(data, '\n')
				sut := NewInterpreter(data)
				sut.filter()
				Expect(sut.source).To(Equal, []byte{' ', '\t', '\n', '\n'})
			})
		})
		Context("ソースファイルをコマンドリストに変換する関数", func() {
			It("スタックに１をプッシュするコマンドが作成されること", func() {
				data = []byte{'P', 'u', 's', 'h', ' ', ' ', '\t', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewSubCommandWithParam("stack", "push", 1))
			})
			It("スタックをコピーするコマンドが作成されること", func() {
				data = []byte{'C', 'o', 'p', 'y', ' ', '\n', ' '}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewSubCommandWithParam("stack", "copy", 0))
			})
		})
	})
}

func current_dir() string {
	_, fpath, _, _ := runtime.Caller(0)
	return path.Dir(fpath)
}
<|endoftext|>


<commit_before>package wspacego

import (
	. "github.com/r7kamura/gospel"
	"path"
	"runtime"
	"testing"
)

func TestInterpretor(t *testing.T) {
	Describe(t, "インタープリターのテスト", func() {
		data := []byte{'A', ' ', 'B', '\t', '\r', '\n'}
		Context("インスタンス生成", func() {
			It("インスタンスが生成できること", func() {
				actual := NewInterpreter(data)
				Expect(actual).To(Exist)
				Expect(actual.origin).To(Equal, data)
			})
		})
		Context("不要な文字を排除する関数", func() {
			It("不要なデータ以外排除されていること", func() {
				sut := NewInterpreter(data)
				sut.filter()
				Expect(sut.source).To(Equal, []byte{' ', '\t', '\n'})
			})
			It("不要なデータ以外排除されていること（改行を増やす）", func() {
				data = append(data, '\n')
				sut := NewInterpreter(data)
				sut.filter()
				Expect(sut.source).To(Equal, []byte{' ', '\t', '\n', '\n'})
			})
		})
		Context("ソースファイルをコマンドリストに変換する関数", func() {
			It("スタックに１をプッシュするコマンドが作成されること", func() {
				data = []byte{'P', 'u', 's', 'h', ' ', ' ', '\t', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewSubCommandWithParam("stack", "push", 1))
			})
			It("スタックをコピーするコマンドが作成されること", func() {
				data = []byte{'C', 'o', 'p', 'y', ' ', '\n', ' '}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewSubCommand("stack", "copy"))
			})
			It("スタックを入れ替えるコマンドが作成されること", func() {
				data = []byte{'S', 'w', 'a', 'p', ' ', '\n', '\t'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewSubCommand("stack", "swap"))
			})
			It("スタックを削除するコマンドが作成されること", func() {
				data = []byte{'R', 'e', 'm', 'o', 'v', 'e', ' ', '\n', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewSubCommand("stack", "remove"))
			})
			It("定義されていない命令が指定されたときにundefinedの命令が作成されること", func() {
				data = []byte{'u', 'n', 'k', 'n', 'o', 'w', 'n', ' ', '\t', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				err := sut.parseCommands()
				Expect(sut.commands).To(NotExist)
				Expect(err).To(Exist)
			})
			It("ラベルを定義するコマンドが作成されること", func() {
				data = []byte{'L', 'a', 'b', 'l', '\n', ' ', ' ', '\t', ' ', ' ', '\t', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewSubCommand("label", "1001"))
			})
			It("ラベルを呼び出すコマンドが作成されること", func() {
				data = []byte{'C', 'a', 'l', 'l', '\n', ' ', '\t', '\t', ' ', ' ', '\t', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewSubCommand("call", "1001"))
			})
			It("ラベルを呼び出すコマンドが作成されること", func() {
				data = []byte{'G', 'o', 't', 'o', '\n', ' ', '\n', '\t', ' ', ' ', '\t', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewSubCommand("goto", "1001"))
			})
			It("スタックの値が０のときにラベルを呼び出すコマンドが作成されること", func() {
				data = []byte{'=', '=', '0', 'G', 'o', 't', 'o', '\n', '\t', ' ', '\t', ' ', ' ', '\t', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewSubCommand("if stack==0 then goto", "1001"))
			})
			It("スタックの値が０未満のときにラベルを呼び出すコマンドが作成されること", func() {
				data = []byte{'<', '0', 'G', 'o', 't', 'o', '\n', '\t', '\t', '\t', ' ', ' ', '\t', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewSubCommand("if stack<0 then goto", "1001"))
			})
			It("呼び出し元に戻るコマンドが作成されること", func() {
				data = []byte{'R', 'e', 't', 'u', 'r', 'n', '\n', '\t', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewCommand("return"))
			})
			It("プログラムを終了するコマンドが作成されること", func() {
				data = []byte{'E', 'x', 'i', 't', '\n', '\n', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewCommand("exit"))
			})
			It("解析できないパターンができたときにエラーが作成されること", func() {
				data = []byte{'u', 'n', 'k', 'o', 'w', 'n', '\n', '\n', '\t'}
				sut := NewInterpreter(data)
				sut.filter()
				err := sut.parseCommands()
				Expect(sut.commands).To(NotExist)
				Expect(err).To(Exist)
			})
			It("足し算する命令が作成されること", func() {
				data = []byte{'a', 'd', 'd', '\t', ' ', ' ', ' '}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewCommand("add"))
			})
			It("引き算する命令が作成されること", func() {
				data = []byte{'s', 'u', 'b', '\t', ' ', ' ', '\t'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewCommand("sub"))
			})
		})
	})
}

func current_dir() string {
	_, fpath, _, _ := runtime.Caller(0)
	return path.Dir(fpath)
}
<commit_msg>掛け算の命令が作成できたことを確認<commit_after>package wspacego

import (
	. "github.com/r7kamura/gospel"
	"path"
	"runtime"
	"testing"
)

func TestInterpretor(t *testing.T) {
	Describe(t, "インタープリターのテスト", func() {
		data := []byte{'A', ' ', 'B', '\t', '\r', '\n'}
		Context("インスタンス生成", func() {
			It("インスタンスが生成できること", func() {
				actual := NewInterpreter(data)
				Expect(actual).To(Exist)
				Expect(actual.origin).To(Equal, data)
			})
		})
		Context("不要な文字を排除する関数", func() {
			It("不要なデータ以外排除されていること", func() {
				sut := NewInterpreter(data)
				sut.filter()
				Expect(sut.source).To(Equal, []byte{' ', '\t', '\n'})
			})
			It("不要なデータ以外排除されていること（改行を増やす）", func() {
				data = append(data, '\n')
				sut := NewInterpreter(data)
				sut.filter()
				Expect(sut.source).To(Equal, []byte{' ', '\t', '\n', '\n'})
			})
		})
		Context("ソースファイルをコマンドリストに変換する関数", func() {
			It("スタックに１をプッシュするコマンドが作成されること", func() {
				data = []byte{'P', 'u', 's', 'h', ' ', ' ', '\t', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewSubCommandWithParam("stack", "push", 1))
			})
			It("スタックをコピーするコマンドが作成されること", func() {
				data = []byte{'C', 'o', 'p', 'y', ' ', '\n', ' '}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewSubCommand("stack", "copy"))
			})
			It("スタックを入れ替えるコマンドが作成されること", func() {
				data = []byte{'S', 'w', 'a', 'p', ' ', '\n', '\t'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewSubCommand("stack", "swap"))
			})
			It("スタックを削除するコマンドが作成されること", func() {
				data = []byte{'R', 'e', 'm', 'o', 'v', 'e', ' ', '\n', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewSubCommand("stack", "remove"))
			})
			It("定義されていない命令が指定されたときにundefinedの命令が作成されること", func() {
				data = []byte{'u', 'n', 'k', 'n', 'o', 'w', 'n', ' ', '\t', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				err := sut.parseCommands()
				Expect(sut.commands).To(NotExist)
				Expect(err).To(Exist)
			})
			It("ラベルを定義するコマンドが作成されること", func() {
				data = []byte{'L', 'a', 'b', 'l', '\n', ' ', ' ', '\t', ' ', ' ', '\t', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewSubCommand("label", "1001"))
			})
			It("ラベルを呼び出すコマンドが作成されること", func() {
				data = []byte{'C', 'a', 'l', 'l', '\n', ' ', '\t', '\t', ' ', ' ', '\t', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewSubCommand("call", "1001"))
			})
			It("ラベルを呼び出すコマンドが作成されること", func() {
				data = []byte{'G', 'o', 't', 'o', '\n', ' ', '\n', '\t', ' ', ' ', '\t', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewSubCommand("goto", "1001"))
			})
			It("スタックの値が０のときにラベルを呼び出すコマンドが作成されること", func() {
				data = []byte{'=', '=', '0', 'G', 'o', 't', 'o', '\n', '\t', ' ', '\t', ' ', ' ', '\t', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewSubCommand("if stack==0 then goto", "1001"))
			})
			It("スタックの値が０未満のときにラベルを呼び出すコマンドが作成されること", func() {
				data = []byte{'<', '0', 'G', 'o', 't', 'o', '\n', '\t', '\t', '\t', ' ', ' ', '\t', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewSubCommand("if stack<0 then goto", "1001"))
			})
			It("呼び出し元に戻るコマンドが作成されること", func() {
				data = []byte{'R', 'e', 't', 'u', 'r', 'n', '\n', '\t', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewCommand("return"))
			})
			It("プログラムを終了するコマンドが作成されること", func() {
				data = []byte{'E', 'x', 'i', 't', '\n', '\n', '\n'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewCommand("exit"))
			})
			It("解析できないパターンができたときにエラーが作成されること", func() {
				data = []byte{'u', 'n', 'k', 'o', 'w', 'n', '\n', '\n', '\t'}
				sut := NewInterpreter(data)
				sut.filter()
				err := sut.parseCommands()
				Expect(sut.commands).To(NotExist)
				Expect(err).To(Exist)
			})
			It("足し算する命令が作成されること", func() {
				data = []byte{'a', 'd', 'd', '\t', ' ', ' ', ' '}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewCommand("add"))
			})
			It("引き算する命令が作成されること", func() {
				data = []byte{'s', 'u', 'b', '\t', ' ', ' ', '\t'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewCommand("sub"))
			})
			It("掛け算する命令が作成されること", func() {
				data = []byte{'m', 'u', 'l', '\t', ' ', ' ', '\t'}
				sut := NewInterpreter(data)
				sut.filter()
				sut.parseCommands()
				Expect(sut.commands).To(Exist)
				Expect(sut.commands.Len()).To(Equal, 1)
				Expect(sut.commands.Get(1)).To(Equal, NewCommand("mul"))
			})
		})
	})
}

func current_dir() string {
	_, fpath, _, _ := runtime.Caller(0)
	return path.Dir(fpath)
}
<|endoftext|>


<commit_before>package main

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

	"github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
)

type server struct {
	collection *ProxyCollection
}

func NewServer(collection *ProxyCollection) *server {
	return &server{
		collection: collection,
	}
}

func (server *server) Listen() {
	r := mux.NewRouter()
	r.HandleFunc("/proxies", server.ProxyIndex).Methods("GET")
	r.HandleFunc("/proxies", server.ProxyCreate).Methods("POST")
	r.HandleFunc("/proxies/{name}", server.ProxyDelete).Methods("DELETE")
	http.Handle("/", r)

	logrus.WithFields(logrus.Fields{
		"host": apiHost,
		"port": apiPort,
	}).Info("API HTTP server started")

	err := http.ListenAndServe(net.JoinHostPort(apiHost, apiPort), nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

func (server *server) ProxyIndex(response http.ResponseWriter, request *http.Request) {
	data, err := json.Marshal(server.collection.Proxies())
	if err != nil {
		http.Error(response, fmt.Sprint(err), http.StatusInternalServerError)
		return
	}

	response.Header().Set("Content-Type", "application/json")
	response.WriteHeader(http.StatusOK)
	_, err = response.Write(data)
	if err != nil {
		logrus.Warn("ProxyIndex: Failed to write response to client", err)
	}
}

func (server *server) ProxyCreate(response http.ResponseWriter, request *http.Request) {
	proxy := NewProxy()
	err := json.NewDecoder(request.Body).Decode(&proxy)
	if err != nil {
		http.Error(response, server.apiError(err, http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	err = server.collection.Add(proxy)
	if err != nil {
		http.Error(response, server.apiError(err, http.StatusConflict), http.StatusConflict)
		return
	}

	proxy.Start()

	data, err := json.Marshal(&proxy)
	if err != nil {
		http.Error(response, server.apiError(err, http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	response.Header().Set("Content-Type", "application/json")
	response.WriteHeader(http.StatusCreated)
	_, err = response.Write(data)
	if err != nil {
		logrus.Warn("ProxyIndex: Failed to write response to client", err)
	}
}

func (server *server) ProxyDelete(response http.ResponseWriter, request *http.Request) {
	vars := mux.Vars(request)

	err := server.collection.Remove(vars["name"])
	if err != nil {
		http.Error(response, server.apiError(err, http.StatusNotFound), http.StatusNotFound)
		return
	}

	response.WriteHeader(http.StatusNoContent)
	_, err = response.Write(nil)
	if err != nil {
		logrus.Warn("ProxyIndex: Failed to write headers to client", err)
	}
}

func (server *server) apiError(err error, code int) string {
	return fmt.Sprintf(`
{
	"title": "%s",
	"status": %d
}
	`, err.Error(), code)
}
<commit_msg>api: modify startup message<commit_after>package main

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

	"github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
)

type server struct {
	collection *ProxyCollection
}

func NewServer(collection *ProxyCollection) *server {
	return &server{
		collection: collection,
	}
}

func (server *server) Listen() {
	r := mux.NewRouter()
	r.HandleFunc("/proxies", server.ProxyIndex).Methods("GET")
	r.HandleFunc("/proxies", server.ProxyCreate).Methods("POST")
	r.HandleFunc("/proxies/{name}", server.ProxyDelete).Methods("DELETE")
	http.Handle("/", r)

	logrus.WithFields(logrus.Fields{
		"host": apiHost,
		"port": apiPort,
	}).Info("API HTTP server starting")

	err := http.ListenAndServe(net.JoinHostPort(apiHost, apiPort), nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

func (server *server) ProxyIndex(response http.ResponseWriter, request *http.Request) {
	data, err := json.Marshal(server.collection.Proxies())
	if err != nil {
		http.Error(response, fmt.Sprint(err), http.StatusInternalServerError)
		return
	}

	response.Header().Set("Content-Type", "application/json")
	response.WriteHeader(http.StatusOK)
	_, err = response.Write(data)
	if err != nil {
		logrus.Warn("ProxyIndex: Failed to write response to client", err)
	}
}

func (server *server) ProxyCreate(response http.ResponseWriter, request *http.Request) {
	proxy := NewProxy()
	err := json.NewDecoder(request.Body).Decode(&proxy)
	if err != nil {
		http.Error(response, server.apiError(err, http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	err = server.collection.Add(proxy)
	if err != nil {
		http.Error(response, server.apiError(err, http.StatusConflict), http.StatusConflict)
		return
	}

	proxy.Start()

	data, err := json.Marshal(&proxy)
	if err != nil {
		http.Error(response, server.apiError(err, http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}

	response.Header().Set("Content-Type", "application/json")
	response.WriteHeader(http.StatusCreated)
	_, err = response.Write(data)
	if err != nil {
		logrus.Warn("ProxyIndex: Failed to write response to client", err)
	}
}

func (server *server) ProxyDelete(response http.ResponseWriter, request *http.Request) {
	vars := mux.Vars(request)

	err := server.collection.Remove(vars["name"])
	if err != nil {
		http.Error(response, server.apiError(err, http.StatusNotFound), http.StatusNotFound)
		return
	}

	response.WriteHeader(http.StatusNoContent)
	_, err = response.Write(nil)
	if err != nil {
		logrus.Warn("ProxyIndex: Failed to write headers to client", err)
	}
}

func (server *server) apiError(err error, code int) string {
	return fmt.Sprintf(`
{
	"title": "%s",
	"status": %d
}
	`, err.Error(), code)
}
<|endoftext|>


<commit_before>package vmx

type Vhardware struct {
	Version int    `vmx:"version,omitempty"`
	Compat  string `vmx:"productcompatibility,omitempty"`
}

type Ethernet struct {
	VMXID                string
	StartConnected       bool   `vmx:"startconnected,omitempty"`
	Present              bool   `vmx:"present,omitempty"`
	ConnectionType       string `vmx:"connectiontype,omitempty"`
	VirtualDev           string `vmx:"virtualdev,omitempty"`
	WakeOnPcktRcv        bool   `vmx:"wakeonpcktrcv,omitempty"`
	AddressType          string `vmx:"addresstype,omitempty"`
	LinkStatePropagation bool   `vmx:"linkstatepropagation.enable,omitempty"`
	VNetwork             string `vmx:"vnet,omitempty"`
}

type Device struct {
	VMXID          string
	Present        bool   `vmx:"present,omitempty"`
	Autodetect     bool   `vmx:"autodetect,omitempty"`
	StartConnected bool   `vmx:"startconnected,omitempty"`
	Type           string `vmx:"devicetype,omitempty"`
	Filename       string `vmxl:"filename,omitempty"`
}

type SATADevice struct {
	Device
}

type SCSIDevice struct {
	Device
	PCISlot    int    `vmx:"pcislotnumber,omitempty"`
	VirtualDev string `vmx:"virtualdev,omitempty"`
}

type IDEDevice struct {
	Device
}

type USBDevice struct {
	VMXID   string
	Present bool   `vmx:"present,omitempty"`
	Speed   uint   `vmx:"speed,omitempty"`
	Type    string `vmx:"devicetype,omitempty"`
	Port    uint   `vmx:"port,omitempty"`
	Parent  string `vmx:"parent,omitmepty"`
}

type PowerType struct {
	PowerOff string `vmx:"poweroff,omitempty"`
	PowerOn  string `vmx:"poweron,omitempty"`
	Reset    string `vmx:"reset,omitempty"`
	Suspend  string `vmx:"suspend,omitempty"`
}

type Sound struct {
	VMXID      string
	Present    bool   `vmx:"present,omitempty"`
	Filename   string `vmx:"filename,omitempty"`
	Autodetect bool   `vmx:"autodetect,omitempty"`
}

type SerialPort struct {
	VMXID              string
	StartConnected     bool   `vmx:"startconnected,omitempty"`
	Present            bool   `vmx:"present,omitempty"`
	Filetype           string `vmx:"filetype,omitempty"`
	Filename           string `vmx:"filename,omitempty"`
	Autodetect         bool   `vmx:"autodetect,omitempty"`
	TryNoRxLoss        bool   `vmx:"trynorxloss,omitempty"`
	PipeEndpoint       string `vmx:"pipe.endpoint,omitempty"`
	AllowGuestConnCtrl bool   `vmx:"allowguestconnectioncontrol,omitempty"`
	HardwareFlowCtrl   bool   `vmx:"hardwareFlowControl,omitempty"`
}

type PCIBridge struct {
	VMXID      string
	Present    bool   `vmx:"present,omitempty"`
	VirtualDev string `vmx:"virtualdev,omitempty"`
	SlotNumber int    `vmx:"pcislotnumber,omitempty"`
	Functions  uint   `vmx:"functions,omitempty"`
}

type Tools struct {
	SyncTime      bool   `vmx:"synctime,omitempty"`
	UpgradePolicy string `vmx:"upgrade.policy,omitempty"`
	RemindInstall bool   `vmx:"remindinstall,omitempty"`
}

type UUID struct {
	Action string `vmx:"action,omitempty"`
	// Autogenerated, do not change
	Bios string `vmx:"bios,omitempty"`
	// Autogenerated, do not change
	Location string `vmx:"location,omitempty"`
}

type RemoteDisplay struct {
	VNCEnabled     bool   `vmx:"vnc.enabled,omitempty"`
	VNCPort        uint   `vmx:"vnc.port,omitempty"`
	VNCPassword    string `vmx:"vnc.password,omitempty"`
	VNCIPAddress   string `vmx:"vnc.ip,omitempty"`
	VNCKey         string `vmx:"vnc.key,omitempty"`
	VNCKeyMap      string `vmx:"vnc.keymap,omitempty"`
	VNCKeyMapFile  string `vmx:"vnc.keymapfile,omitempty"`
	VNCZlibLevel   uint   `vmx:"vnc.zliblevel,omitempty"`
	VNCWheelStep   string `vmx:"vncWheelStep,omitempty"`
	Depth          uint   `vmx:"depth,omitempty"`
	MaxConnections uint   `vmx:"maxconnections,omitempty"`
	MaxHeight      uint   `vmx:"maxheight,omitempty"`
	MaxWidth       uint   `vmx:"maxwidth,omitempty"`
}

type SharedFolder struct {
	VMXID       string
	Present     bool   `vmx:"present,omitempty"`
	Enabled     bool   `vmx:"enabled,omitempty"`
	ReadAccess  bool   `vmx:"readaccess,omitempty"`
	WriteAccess bool   `vmx:"writeaccess,omitempty"`
	HostPath    string `vmx:"hostpath,omitempty"`
	GuestName   string `vmx:"guestname,omitempty"`
	Expiration  string `vmx:"expiration,omitempty"`
}

type GUI struct {
	ExitAtPowerOff      bool `vmx:"exitatpoweroff,omitempty"`
	FullScreenAtPowerOn bool `vmx:"fullscreenatpoweron,omitempty"`
	PowerOnAtStartup    bool `vmx:"poweronatstartup,omitempty"`
	ExitOnCLIHalt       bool `vmx:"exitonclihlt,omitempty"`
}

type Isolation struct {
	// Disable shared folders
	HgfsDisable      bool `vmx:"tools.hgfs.disable,omitempty"`
	CopyDisable      bool `vmx:"tools.copy.disable,omitempty"`
	PasteDisable     bool `vmx:"tools.paste.disable,omitempty"`
	DragNDropDisable bool `vmx:"tools.dnd.disable,omitempty"`
}

type FloppyDevice struct {
	VMXID          string
	Present        bool   `vmx:"present,omitempty"`
	StartConnected bool   `vmx:"startconnected,omitempty"`
	Autodetect     bool   `vmx:"autodetect,omitempty"`
	Filename       string `vmx:"filename,omitempty"`
	Filetype       string `vmx:"filetype,omitempty"`
	GuestControl   bool   `vmx:"allowGuestConnectionControl,omitempty"`
}

type VMCI struct {
	VMXID   string
	ID      string `vmx:"id,omitempty"`
	Present bool   `vmx:"present,omitempty"`
	PCISlot int    `vmx:"pcislotnumber,omitempty"`
}

type VirtualMachine struct {
	Encoding        string    `vmx:".encoding,omitempty"`
	ExtendedCfgFile string    `vmx:"extendedconfigfile,omitempty"`
	PowerType       PowerType `vmx:"powertype,omitempty"`
	Annotation      string    `vmx:"annotation,omitempty"`
	Vhardware       Vhardware `vmx:"virtualhw,omitempty"`
	Memsize         uint      `vmx:"memsize,omitempty"`
	NumvCPUs        uint      `vmx:"numvcpus,omitempty"`
	MemHotAdd       bool      `vmx:"mem.hotadd,omitempty"`
	VCPUHotAdd      bool      `vmx:"vcpu.hotadd,omitempty"`
	DisplayName     string    `vmx:"displayname,omitempty"`
	GuestOS         string    `vmx:"guestos,omitempty"`
	Autoanswer      bool      `vmx:"msg.autoanswer,omitempty"`
	Sound           Sound     `vmx:"sound,omitempty"`
	Tools           Tools     `vmx:"tools,omitempty"`
	NVRam           string    `vmx:"nvmram,omitempty"`
	UUID            UUID      `vmx:"uuid,omitempty"`
	CleanShutdown   bool      `vmx:"cleanshutdown,omitempty"`
	SoftPowerOff    bool      `vmx:"softpoweroff,omitempty"`
	VMCI            VMCI      `vmx:"vmci0,omitempty"`
	// Enable or not nested virtualiation
	VHVEnable     bool           `vmx:"vhv.enable,omitempty"`
	RemoteDisplay RemoteDisplay  `vmx:"remotedisplay,omitempty"`
	Isolation     Isolation      `vmx:"isolation,omitempty"`
	SharedFolders []SharedFolder `vmx:"sharedfolder,omitempty"`
	PCIBridges    []PCIBridge    `vmx:"pcibridge,omitempty"`
	SerialPorts   []SerialPort   `vmx:"serial,omitempty"`
	Ethernet      []Ethernet     `vmx:"ethernet,omitempty"`
	IDEDevices    []IDEDevice    `vmx:"ide,omitempty"`
	SCSIDevices   []SCSIDevice   `vmx:"scsi,omitempty"`
	SATADevices   []SATADevice   `vmx:"sata,omitempty"`
	USBDevices    []USBDevice    `vmx:"usb,omitempty"`
	FloppyDevices []FloppyDevice `vmx:"floppy,omitempty"`
}

func (vm VirtualMachine) WalkDevices(f func(Device) bool, types ...string) bool {
	var sata, ide, scsi bool
	for _, t := range types {
		switch t {
		case "sata":
			sata = true
		case "ide":
			ide = true
		case "scsi":
			scsi = true
		}
	}
	if len(types) == 0 {
		sata, ide, scsi = true, true, true
	}
	if ide {
		for _, d := range vm.IDEDevices {
			if f(d.Device) {
				return true
			}
		}
	}
	if scsi {
		for _, d := range vm.SCSIDevices {
			if f(d.Device) {
				return true
			}
		}
	}
	if sata {
		for _, d := range vm.SATADevices {
			if f(d.Device) {
				return true
			}
		}
	}
	return false
}
<commit_msg>added WalkDevices and Find<commit_after>package vmx

type Vhardware struct {
	Version int    `vmx:"version,omitempty"`
	Compat  string `vmx:"productcompatibility,omitempty"`
}

type Ethernet struct {
	VMXID                string
	StartConnected       bool   `vmx:"startconnected,omitempty"`
	Present              bool   `vmx:"present,omitempty"`
	ConnectionType       string `vmx:"connectiontype,omitempty"`
	VirtualDev           string `vmx:"virtualdev,omitempty"`
	WakeOnPcktRcv        bool   `vmx:"wakeonpcktrcv,omitempty"`
	AddressType          string `vmx:"addresstype,omitempty"`
	LinkStatePropagation bool   `vmx:"linkstatepropagation.enable,omitempty"`
	VNetwork             string `vmx:"vnet,omitempty"`
}

type Device struct {
	VMXID          string
	Present        bool   `vmx:"present,omitempty"`
	Autodetect     bool   `vmx:"autodetect,omitempty"`
	StartConnected bool   `vmx:"startconnected,omitempty"`
	Type           string `vmx:"devicetype,omitempty"`
	Filename       string `vmxl:"filename,omitempty"`
}

type SATADevice struct {
	Device
}

type SCSIDevice struct {
	Device
	PCISlot    int    `vmx:"pcislotnumber,omitempty"`
	VirtualDev string `vmx:"virtualdev,omitempty"`
}

type IDEDevice struct {
	Device
}

type USBDevice struct {
	VMXID   string
	Present bool   `vmx:"present,omitempty"`
	Speed   uint   `vmx:"speed,omitempty"`
	Type    string `vmx:"devicetype,omitempty"`
	Port    uint   `vmx:"port,omitempty"`
	Parent  string `vmx:"parent,omitmepty"`
}

type PowerType struct {
	PowerOff string `vmx:"poweroff,omitempty"`
	PowerOn  string `vmx:"poweron,omitempty"`
	Reset    string `vmx:"reset,omitempty"`
	Suspend  string `vmx:"suspend,omitempty"`
}

type Sound struct {
	VMXID      string
	Present    bool   `vmx:"present,omitempty"`
	Filename   string `vmx:"filename,omitempty"`
	Autodetect bool   `vmx:"autodetect,omitempty"`
}

type SerialPort struct {
	VMXID              string
	StartConnected     bool   `vmx:"startconnected,omitempty"`
	Present            bool   `vmx:"present,omitempty"`
	Filetype           string `vmx:"filetype,omitempty"`
	Filename           string `vmx:"filename,omitempty"`
	Autodetect         bool   `vmx:"autodetect,omitempty"`
	TryNoRxLoss        bool   `vmx:"trynorxloss,omitempty"`
	PipeEndpoint       string `vmx:"pipe.endpoint,omitempty"`
	AllowGuestConnCtrl bool   `vmx:"allowguestconnectioncontrol,omitempty"`
	HardwareFlowCtrl   bool   `vmx:"hardwareFlowControl,omitempty"`
}

type PCIBridge struct {
	VMXID      string
	Present    bool   `vmx:"present,omitempty"`
	VirtualDev string `vmx:"virtualdev,omitempty"`
	SlotNumber int    `vmx:"pcislotnumber,omitempty"`
	Functions  uint   `vmx:"functions,omitempty"`
}

type Tools struct {
	SyncTime      bool   `vmx:"synctime,omitempty"`
	UpgradePolicy string `vmx:"upgrade.policy,omitempty"`
	RemindInstall bool   `vmx:"remindinstall,omitempty"`
}

type UUID struct {
	Action string `vmx:"action,omitempty"`
	// Autogenerated, do not change
	Bios string `vmx:"bios,omitempty"`
	// Autogenerated, do not change
	Location string `vmx:"location,omitempty"`
}

type RemoteDisplay struct {
	VNCEnabled     bool   `vmx:"vnc.enabled,omitempty"`
	VNCPort        uint   `vmx:"vnc.port,omitempty"`
	VNCPassword    string `vmx:"vnc.password,omitempty"`
	VNCIPAddress   string `vmx:"vnc.ip,omitempty"`
	VNCKey         string `vmx:"vnc.key,omitempty"`
	VNCKeyMap      string `vmx:"vnc.keymap,omitempty"`
	VNCKeyMapFile  string `vmx:"vnc.keymapfile,omitempty"`
	VNCZlibLevel   uint   `vmx:"vnc.zliblevel,omitempty"`
	VNCWheelStep   string `vmx:"vncWheelStep,omitempty"`
	Depth          uint   `vmx:"depth,omitempty"`
	MaxConnections uint   `vmx:"maxconnections,omitempty"`
	MaxHeight      uint   `vmx:"maxheight,omitempty"`
	MaxWidth       uint   `vmx:"maxwidth,omitempty"`
}

type SharedFolder struct {
	VMXID       string
	Present     bool   `vmx:"present,omitempty"`
	Enabled     bool   `vmx:"enabled,omitempty"`
	ReadAccess  bool   `vmx:"readaccess,omitempty"`
	WriteAccess bool   `vmx:"writeaccess,omitempty"`
	HostPath    string `vmx:"hostpath,omitempty"`
	GuestName   string `vmx:"guestname,omitempty"`
	Expiration  string `vmx:"expiration,omitempty"`
}

type GUI struct {
	ExitAtPowerOff      bool `vmx:"exitatpoweroff,omitempty"`
	FullScreenAtPowerOn bool `vmx:"fullscreenatpoweron,omitempty"`
	PowerOnAtStartup    bool `vmx:"poweronatstartup,omitempty"`
	ExitOnCLIHalt       bool `vmx:"exitonclihlt,omitempty"`
}

type Isolation struct {
	// Disable shared folders
	HgfsDisable      bool `vmx:"tools.hgfs.disable,omitempty"`
	CopyDisable      bool `vmx:"tools.copy.disable,omitempty"`
	PasteDisable     bool `vmx:"tools.paste.disable,omitempty"`
	DragNDropDisable bool `vmx:"tools.dnd.disable,omitempty"`
}

type FloppyDevice struct {
	VMXID          string
	Present        bool   `vmx:"present,omitempty"`
	StartConnected bool   `vmx:"startconnected,omitempty"`
	Autodetect     bool   `vmx:"autodetect,omitempty"`
	Filename       string `vmx:"filename,omitempty"`
	Filetype       string `vmx:"filetype,omitempty"`
	GuestControl   bool   `vmx:"allowGuestConnectionControl,omitempty"`
}

type VMCI struct {
	VMXID   string
	ID      string `vmx:"id,omitempty"`
	Present bool   `vmx:"present,omitempty"`
	PCISlot int    `vmx:"pcislotnumber,omitempty"`
}

type VirtualMachine struct {
	Encoding        string    `vmx:".encoding,omitempty"`
	ExtendedCfgFile string    `vmx:"extendedconfigfile,omitempty"`
	PowerType       PowerType `vmx:"powertype,omitempty"`
	Annotation      string    `vmx:"annotation,omitempty"`
	Vhardware       Vhardware `vmx:"virtualhw,omitempty"`
	Memsize         uint      `vmx:"memsize,omitempty"`
	NumvCPUs        uint      `vmx:"numvcpus,omitempty"`
	MemHotAdd       bool      `vmx:"mem.hotadd,omitempty"`
	VCPUHotAdd      bool      `vmx:"vcpu.hotadd,omitempty"`
	DisplayName     string    `vmx:"displayname,omitempty"`
	GuestOS         string    `vmx:"guestos,omitempty"`
	Autoanswer      bool      `vmx:"msg.autoanswer,omitempty"`
	Sound           Sound     `vmx:"sound,omitempty"`
	Tools           Tools     `vmx:"tools,omitempty"`
	NVRam           string    `vmx:"nvmram,omitempty"`
	UUID            UUID      `vmx:"uuid,omitempty"`
	CleanShutdown   bool      `vmx:"cleanshutdown,omitempty"`
	SoftPowerOff    bool      `vmx:"softpoweroff,omitempty"`
	VMCI            VMCI      `vmx:"vmci0,omitempty"`
	// Enable or not nested virtualiation
	VHVEnable     bool           `vmx:"vhv.enable,omitempty"`
	RemoteDisplay RemoteDisplay  `vmx:"remotedisplay,omitempty"`
	Isolation     Isolation      `vmx:"isolation,omitempty"`
	SharedFolders []SharedFolder `vmx:"sharedfolder,omitempty"`
	PCIBridges    []PCIBridge    `vmx:"pcibridge,omitempty"`
	SerialPorts   []SerialPort   `vmx:"serial,omitempty"`
	Ethernet      []Ethernet     `vmx:"ethernet,omitempty"`
	IDEDevices    []IDEDevice    `vmx:"ide,omitempty"`
	SCSIDevices   []SCSIDevice   `vmx:"scsi,omitempty"`
	SATADevices   []SATADevice   `vmx:"sata,omitempty"`
	USBDevices    []USBDevice    `vmx:"usb,omitempty"`
	FloppyDevices []FloppyDevice `vmx:"floppy,omitempty"`
}

// Bus type to use when attaching CD/DVD drives and disks.
type BusType string

// Disk controllers
const (
	IDE  BusType = "ide"
	SCSI BusType = "scsi"
	SATA BusType = "sata"
)

// Find executes the given function p on all the devices of one of the given
// types until one of the calls returns true.
// Find returns true only if one of the calls of p returned true.
func (vm VirtualMachine) Find(p func(Device) bool, types ...BusType) bool {
	return vm.walkDevices(p, types...)
}

// WalkDevices executes the given function f on all the devices of one of the
// specified types.
func (vm VirtualMachine) WalkDevices(f func(Device), types ...BusType) {
	p := func(d Device) bool { f(d); return false }
	vm.walkDevices(p, types...)
}

func (vm VirtualMachine) walkDevices(p func(Device) bool, types ...BusType) bool {
	for _, t := range types {
		switch t {
		case SATA:
			for _, d := range vm.IDEDevices {
				if p(d.Device) {
					return true
				}
			}
		case IDE:
			for _, d := range vm.SCSIDevices {
				if p(d.Device) {
					return true
				}
			}
		case SCSI:
			for _, d := range vm.SATADevices {
				if p(d.Device) {
					return true
				}
			}
		}
	}
	return false
}
<|endoftext|>


<commit_before>package papaBot

// Public bot API.

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/pawelszydlo/papa-bot/events"
	"github.com/pawelszydlo/papa-bot/transports"
	"github.com/pawelszydlo/papa-bot/utils"
	"golang.org/x/net/html/charset"
	"golang.org/x/text/transform"
	"html"
	"io"
	"net/http"
	"reflect"
	"strings"
	"text/template"
	"time"
)

// RegisterExtension will register a new transport with the bot.
func (bot *Bot) RegisterTransport(transport transports.Transport) {
	// Is the transport enabled in the config?
	name := transport.Name()
	if bot.fullConfig.GetDefault(fmt.Sprintf("%s.enabled", name), false).(bool) {
		for existingName := range bot.transports {
			if name == existingName {
				bot.Log.Fatalf("Transport with name '%s' is already registered.", name)
			}
		}
		bot.transports[name] = transport
		bot.Log.Infof("Added transport: %s", name)
	} else {
		bot.Log.Infof("Transport with name '%s' disabled in the config.", name)
	}
}

// RegisterExtension will register a new extension with the bot.
func (bot *Bot) RegisterExtension(ext extension) {
	if ext == nil {
		bot.Log.Fatal("Nil extension provided.")
	}
	bot.extensions = append(bot.extensions, ext)
	bot.Log.Debugf("Added extension: %T", ext)
	// If bot's init was already done, all other extensions have already been initialized.
	if bot.initDone {
		if err := ext.Init(bot); err != nil {
			bot.Log.Fatalf("Error initializing extension: %s", err)
		}
	}
}

// RegisterCommand will register a new command with the bot.
func (bot *Bot) RegisterCommand(cmd *BotCommand) {
	for _, name := range cmd.CommandNames {
		for existingName := range bot.commands {
			if name == existingName {
				bot.Log.Fatalf("Command under alias '%s' already exists.", name)
			}
		}
		bot.commands[name] = cmd
	}
}

// SendMessage sends a message to the channel.
func (bot *Bot) SendMessage(sourceEvent *events.EventMessage, message string) {
	bot.Log.Debugf("Sending message to [%s]%s: %s", sourceEvent.TransportName, sourceEvent.Channel, message)
	transport := bot.getTransportOrDie(sourceEvent.TransportName)
	transport.SendMessage(sourceEvent, message)
}

// SendPrivateMessage sends a message directly to the user.
func (bot *Bot) SendPrivateMessage(sourceEvent *events.EventMessage, nick, message string) {
	bot.Log.Debugf("Sending private message to [%s]%s: %s", sourceEvent.TransportName, nick, message)
	transport := bot.getTransportOrDie(sourceEvent.TransportName)
	transport.SendPrivateMessage(sourceEvent, nick, message)
}

// SendNotice sends a notice to the channel.
func (bot *Bot) SendNotice(sourceEvent *events.EventMessage, message string) {
	bot.Log.Debugf("Sending notice to [%s]%s: %s", sourceEvent.TransportName, sourceEvent.Channel, message)
	transport := bot.getTransportOrDie(sourceEvent.TransportName)
	transport.SendNotice(sourceEvent, message)
}

// SendMassNotice sends a notice to all the channels bot is on, on all transports.
func (bot *Bot) SendMassNotice(message string) {
	bot.Log.Debugf("Sending mass notice: %s", message)
	for _, transport := range bot.transports {
		transport.SendMassNotice(message)
	}
}

// GetPageBody gets and returns a body of a page. Return format is error, final url, body.
func (bot *Bot) GetPageBody(URL string, customHeaders map[string]string) (error, string, []byte) {
	if URL == "" {
		return errors.New("Empty URL"), "", nil
	}
	// Build the request.
	req, err := http.NewRequest("GET", URL, nil)
	if err != nil {
		return err, "", nil
	}
	if customHeaders == nil {
		customHeaders = map[string]string{}
	}
	if customHeaders["User-Agent"] == "" {
		customHeaders["User-Agent"] = bot.Config.HttpDefaultUserAgent
	}
	for k, v := range customHeaders {
		req.Header.Set(k, v)
	}
	fmt.Println(req.Header)

	// Get response.
	bot.Log.Debugf("Fetching page: %s", URL)
	resp, err := bot.HTTPClient.Do(req)
	if err != nil {
		return err, "", nil
	}
	if resp.StatusCode >= 400 {
		bot.Log.Warnf("Got HTTP response: %s", resp.Status)
		return errors.New(resp.Status), "", nil
	}
	defer resp.Body.Close()

	// Update the URL if it changed after redirects.
	finalLink := resp.Request.URL.String()
	if finalLink != "" && finalLink != URL {
		bot.Log.Debugf("%s becomes %s", URL, finalLink)
		URL = finalLink
	}

	// Load the body up to PageBodyMaxSize.
	body := make([]byte, bot.Config.PageBodyMaxSize, bot.Config.PageBodyMaxSize)
	if num, err := io.ReadFull(resp.Body, body); err != nil && err != io.ErrUnexpectedEOF {
		return err, finalLink, nil
	} else {
		// Trim unneeded 0 bytes so that JSON unmarshaller won't complain.
		body = body[:num]
	}
	// Get the content-type
	contentType := resp.Header.Get("Content-Type")
	if contentType == "" {
		contentType = http.DetectContentType(body)
	}

	// If type is text, decode the body to UTF-8.
	if strings.Contains(contentType, "text/") {
		// Try to get more significant part for encoding detection.
		sample := bytes.Join(bot.webContentSampleRe.FindAll(body, -1), []byte{})
		if len(sample) < 100 {
			sample = body
		}
		// Unescape HTML tokens.
		sample = []byte(html.UnescapeString(string(sample)))
		// Try to only get charset from content type. Needed because some pages serve broken Content-Type header.
		detectionContentType := contentType
		tokens := strings.Split(contentType, ";")
		for _, t := range tokens {
			if strings.Contains(strings.ToLower(t), "charset") {
				detectionContentType = "text/plain; " + t
				break
			}
		}
		// Detect encoding and transform.
		encoding, _, _ := charset.DetermineEncoding(sample, detectionContentType)
		decodedBody, _, _ := transform.Bytes(encoding.NewDecoder(), body)
		return nil, finalLink, decodedBody
	} else if strings.Contains(contentType, "application/json") {
		return nil, finalLink, body
	} else {
		bot.Log.Debugf("Not fetching the body for Content-Type: %s", contentType)
	}
	return nil, "", nil
}

// LoadTexts loads texts from a section of a config file into a struct, auto handling templates and lists.
// The name of the field in the data struct defines the name in the config file.
// The type of the field determines the expected config value.
func (bot *Bot) LoadTexts(section string, data interface{}) error {

	reflectedData := reflect.ValueOf(data).Elem()

	for i := 0; i < reflectedData.NumField(); i++ {
		fieldDef := reflectedData.Type().Field(i)
		// Get the field name.
		fieldName := fieldDef.Name
		// Get the field type name.
		fieldType := fmt.Sprint(fieldDef.Type)
		// Get the field itself.
		field := reflectedData.FieldByName(fieldName)
		if !field.CanSet() {
			bot.Log.Fatalf("Field %s is not settable.", fieldName)
		}

		// Load configured text for the field.
		key := fmt.Sprintf("%s.%s", section, fieldName)
		if !bot.fullTexts.Has(key) {
			bot.Log.Fatalf("Couldn't load text for field %s, key %s.", fieldName, key)
		}

		if fieldType == "*template.Template" { // This field is a template.
			temp, err := template.New(fieldName).Parse(bot.fullTexts.Get(key).(string))
			if err != nil {
				return err
			} else {
				field.Set(reflect.ValueOf(temp))
			}
		} else if fieldType == "string" { // Regular text field.
			field.Set(reflect.ValueOf(bot.fullTexts.Get(key).(string)))
		} else if fieldType == "[]string" {
			field.Set(reflect.ValueOf(utils.ToStringSlice(bot.fullTexts.Get(key).([]interface{}))))
		} else {
			bot.Log.Fatalf("Unsupported type of text field: %s", fieldType)
		}
	}

	return nil
}

// SetVar will set a custom variable. Set to empty string to delete.
func (bot *Bot) SetVar(name, value string) {
	if name == "" {
		return
	}
	// Delete.
	if value == "" {
		delete(bot.customVars, name)
		if _, err := bot.Db.Exec(`DELETE FROM vars WHERE name=?`, name); err != nil {
			bot.Log.Errorf("Can't delete custom variable %s: %s", name, err)
		}
		return
	}
	bot.customVars[name] = value
	if _, err := bot.Db.Exec(`INSERT OR REPLACE INTO vars VALUES(?, ?)`, name, value); err != nil {
		bot.Log.Errorf("Can't add custom variable %s: %s", name, err)
	}
}

// GetVar returns the value of a custom variable.
func (bot *Bot) GetVar(name string) string {
	return bot.customVars[name]
}

// AddMoreInfo will set more information to be viewed for the channel.
func (bot *Bot) AddMoreInfo(transport, channel, info string) error {
	bot.urlMoreInfo[transport+channel] = info
	return nil
}

// NextDailyTick will get the time for bot's next daily tick.
func (bot *Bot) NextDailyTick() time.Time {
	tick := bot.nextDailyTick
	return tick
}

// AddToIgnoreList will add a user to the ignore list.
func (bot *Bot) AddToIgnoreList(userId string) {
	ignored := strings.Split(bot.GetVar("_ignored"), " ")
	ignored = utils.RemoveDuplicates(append(ignored, userId))
	bot.SetVar("_ignored", strings.Join(ignored, " "))
	// Update the actual blocklist in the event handler.
	bot.EventDispatcher.SetBlackList(ignored)
	bot.Log.Infof("%s added to ignore list.", userId)
}

// RemoveFromIgnoreList will remove user from the ignore list.
func (bot *Bot) RemoveFromIgnoreList(userId string) {
	ignored := strings.Split(bot.GetVar("_ignored"), " ")
	ignored = utils.RemoveFromSlice(ignored, userId)
	bot.SetVar("_ignored", strings.Join(ignored, " "))
	// Update the actual blocklist in the event handler.
	bot.EventDispatcher.SetBlackList(ignored)
	bot.Log.Infof("%s removed from ignore list.", userId)
}
<commit_msg>Fix url handling on unsupported mime types.<commit_after>package papaBot

// Public bot API.

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/pawelszydlo/papa-bot/events"
	"github.com/pawelszydlo/papa-bot/transports"
	"github.com/pawelszydlo/papa-bot/utils"
	"golang.org/x/net/html/charset"
	"golang.org/x/text/transform"
	"html"
	"io"
	"net/http"
	"reflect"
	"strings"
	"text/template"
	"time"
)

// RegisterExtension will register a new transport with the bot.
func (bot *Bot) RegisterTransport(transport transports.Transport) {
	// Is the transport enabled in the config?
	name := transport.Name()
	if bot.fullConfig.GetDefault(fmt.Sprintf("%s.enabled", name), false).(bool) {
		for existingName := range bot.transports {
			if name == existingName {
				bot.Log.Fatalf("Transport with name '%s' is already registered.", name)
			}
		}
		bot.transports[name] = transport
		bot.Log.Infof("Added transport: %s", name)
	} else {
		bot.Log.Infof("Transport with name '%s' disabled in the config.", name)
	}
}

// RegisterExtension will register a new extension with the bot.
func (bot *Bot) RegisterExtension(ext extension) {
	if ext == nil {
		bot.Log.Fatal("Nil extension provided.")
	}
	bot.extensions = append(bot.extensions, ext)
	bot.Log.Debugf("Added extension: %T", ext)
	// If bot's init was already done, all other extensions have already been initialized.
	if bot.initDone {
		if err := ext.Init(bot); err != nil {
			bot.Log.Fatalf("Error initializing extension: %s", err)
		}
	}
}

// RegisterCommand will register a new command with the bot.
func (bot *Bot) RegisterCommand(cmd *BotCommand) {
	for _, name := range cmd.CommandNames {
		for existingName := range bot.commands {
			if name == existingName {
				bot.Log.Fatalf("Command under alias '%s' already exists.", name)
			}
		}
		bot.commands[name] = cmd
	}
}

// SendMessage sends a message to the channel.
func (bot *Bot) SendMessage(sourceEvent *events.EventMessage, message string) {
	bot.Log.Debugf("Sending message to [%s]%s: %s", sourceEvent.TransportName, sourceEvent.Channel, message)
	transport := bot.getTransportOrDie(sourceEvent.TransportName)
	transport.SendMessage(sourceEvent, message)
}

// SendPrivateMessage sends a message directly to the user.
func (bot *Bot) SendPrivateMessage(sourceEvent *events.EventMessage, nick, message string) {
	bot.Log.Debugf("Sending private message to [%s]%s: %s", sourceEvent.TransportName, nick, message)
	transport := bot.getTransportOrDie(sourceEvent.TransportName)
	transport.SendPrivateMessage(sourceEvent, nick, message)
}

// SendNotice sends a notice to the channel.
func (bot *Bot) SendNotice(sourceEvent *events.EventMessage, message string) {
	bot.Log.Debugf("Sending notice to [%s]%s: %s", sourceEvent.TransportName, sourceEvent.Channel, message)
	transport := bot.getTransportOrDie(sourceEvent.TransportName)
	transport.SendNotice(sourceEvent, message)
}

// SendMassNotice sends a notice to all the channels bot is on, on all transports.
func (bot *Bot) SendMassNotice(message string) {
	bot.Log.Debugf("Sending mass notice: %s", message)
	for _, transport := range bot.transports {
		transport.SendMassNotice(message)
	}
}

// GetPageBody gets and returns a body of a page. Return format is error, final url, body.
func (bot *Bot) GetPageBody(URL string, customHeaders map[string]string) (error, string, []byte) {
	if URL == "" {
		return errors.New("Empty URL"), "", nil
	}
	// Build the request.
	req, err := http.NewRequest("GET", URL, nil)
	if err != nil {
		return err, "", nil
	}
	if customHeaders == nil {
		customHeaders = map[string]string{}
	}
	if customHeaders["User-Agent"] == "" {
		customHeaders["User-Agent"] = bot.Config.HttpDefaultUserAgent
	}
	for k, v := range customHeaders {
		req.Header.Set(k, v)
	}
	fmt.Println(req.Header)

	// Get response.
	bot.Log.Debugf("Fetching page: %s", URL)
	resp, err := bot.HTTPClient.Do(req)
	if err != nil {
		return err, "", nil
	}
	if resp.StatusCode >= 400 {
		bot.Log.Warnf("Got HTTP response: %s", resp.Status)
		return errors.New(resp.Status), "", nil
	}
	defer resp.Body.Close()

	// Update the URL if it changed after redirects.
	finalLink := resp.Request.URL.String()
	if finalLink != "" && finalLink != URL {
		bot.Log.Debugf("%s becomes %s", URL, finalLink)
		URL = finalLink
	}

	// Load the body up to PageBodyMaxSize.
	body := make([]byte, bot.Config.PageBodyMaxSize, bot.Config.PageBodyMaxSize)
	if num, err := io.ReadFull(resp.Body, body); err != nil && err != io.ErrUnexpectedEOF {
		return err, URL, nil
	} else {
		// Trim unneeded 0 bytes so that JSON unmarshaller won't complain.
		body = body[:num]
	}
	// Get the content-type
	contentType := resp.Header.Get("Content-Type")
	if contentType == "" {
		contentType = http.DetectContentType(body)
	}

	// If type is text, decode the body to UTF-8.
	if strings.Contains(contentType, "text/") {
		// Try to get more significant part for encoding detection.
		sample := bytes.Join(bot.webContentSampleRe.FindAll(body, -1), []byte{})
		if len(sample) < 100 {
			sample = body
		}
		// Unescape HTML tokens.
		sample = []byte(html.UnescapeString(string(sample)))
		// Try to only get charset from content type. Needed because some pages serve broken Content-Type header.
		detectionContentType := contentType
		tokens := strings.Split(contentType, ";")
		for _, t := range tokens {
			if strings.Contains(strings.ToLower(t), "charset") {
				detectionContentType = "text/plain; " + t
				break
			}
		}
		// Detect encoding and transform.
		encoding, _, _ := charset.DetermineEncoding(sample, detectionContentType)
		decodedBody, _, _ := transform.Bytes(encoding.NewDecoder(), body)
		return nil, URL, decodedBody
	} else if strings.Contains(contentType, "application/json") {
		return nil, URL, body
	} else {
		bot.Log.Debugf("Not fetching the body for Content-Type: %s", contentType)
	}
	return nil, URL, nil
}

// LoadTexts loads texts from a section of a config file into a struct, auto handling templates and lists.
// The name of the field in the data struct defines the name in the config file.
// The type of the field determines the expected config value.
func (bot *Bot) LoadTexts(section string, data interface{}) error {

	reflectedData := reflect.ValueOf(data).Elem()

	for i := 0; i < reflectedData.NumField(); i++ {
		fieldDef := reflectedData.Type().Field(i)
		// Get the field name.
		fieldName := fieldDef.Name
		// Get the field type name.
		fieldType := fmt.Sprint(fieldDef.Type)
		// Get the field itself.
		field := reflectedData.FieldByName(fieldName)
		if !field.CanSet() {
			bot.Log.Fatalf("Field %s is not settable.", fieldName)
		}

		// Load configured text for the field.
		key := fmt.Sprintf("%s.%s", section, fieldName)
		if !bot.fullTexts.Has(key) {
			bot.Log.Fatalf("Couldn't load text for field %s, key %s.", fieldName, key)
		}

		if fieldType == "*template.Template" { // This field is a template.
			temp, err := template.New(fieldName).Parse(bot.fullTexts.Get(key).(string))
			if err != nil {
				return err
			} else {
				field.Set(reflect.ValueOf(temp))
			}
		} else if fieldType == "string" { // Regular text field.
			field.Set(reflect.ValueOf(bot.fullTexts.Get(key).(string)))
		} else if fieldType == "[]string" {
			field.Set(reflect.ValueOf(utils.ToStringSlice(bot.fullTexts.Get(key).([]interface{}))))
		} else {
			bot.Log.Fatalf("Unsupported type of text field: %s", fieldType)
		}
	}

	return nil
}

// SetVar will set a custom variable. Set to empty string to delete.
func (bot *Bot) SetVar(name, value string) {
	if name == "" {
		return
	}
	// Delete.
	if value == "" {
		delete(bot.customVars, name)
		if _, err := bot.Db.Exec(`DELETE FROM vars WHERE name=?`, name); err != nil {
			bot.Log.Errorf("Can't delete custom variable %s: %s", name, err)
		}
		return
	}
	bot.customVars[name] = value
	if _, err := bot.Db.Exec(`INSERT OR REPLACE INTO vars VALUES(?, ?)`, name, value); err != nil {
		bot.Log.Errorf("Can't add custom variable %s: %s", name, err)
	}
}

// GetVar returns the value of a custom variable.
func (bot *Bot) GetVar(name string) string {
	return bot.customVars[name]
}

// AddMoreInfo will set more information to be viewed for the channel.
func (bot *Bot) AddMoreInfo(transport, channel, info string) error {
	bot.urlMoreInfo[transport+channel] = info
	return nil
}

// NextDailyTick will get the time for bot's next daily tick.
func (bot *Bot) NextDailyTick() time.Time {
	tick := bot.nextDailyTick
	return tick
}

// AddToIgnoreList will add a user to the ignore list.
func (bot *Bot) AddToIgnoreList(userId string) {
	ignored := strings.Split(bot.GetVar("_ignored"), " ")
	ignored = utils.RemoveDuplicates(append(ignored, userId))
	bot.SetVar("_ignored", strings.Join(ignored, " "))
	// Update the actual blocklist in the event handler.
	bot.EventDispatcher.SetBlackList(ignored)
	bot.Log.Infof("%s added to ignore list.", userId)
}

// RemoveFromIgnoreList will remove user from the ignore list.
func (bot *Bot) RemoveFromIgnoreList(userId string) {
	ignored := strings.Split(bot.GetVar("_ignored"), " ")
	ignored = utils.RemoveFromSlice(ignored, userId)
	bot.SetVar("_ignored", strings.Join(ignored, " "))
	// Update the actual blocklist in the event handler.
	bot.EventDispatcher.SetBlackList(ignored)
	bot.Log.Infof("%s removed from ignore list.", userId)
}
<|endoftext|>


<commit_before>package reply

import (
	"regexp"
	"math/rand"
	"time"

	"github.com/tucnak/telebot"
	"github.com/asdine/storm"

	"github.com/focusshifter/muxgoob/registry"
)

type ReplyPlugin struct {
}

var db *storm.DB
var rng *rand.Rand

func init() {
	registry.RegisterPlugin(&ReplyPlugin{})
}

func (p *ReplyPlugin) Start(sharedDb *storm.DB) {
	db = sharedDb
    rng = rand.New(rand.NewSource(time.Now().UnixNano()))
}

func (p *ReplyPlugin) Run(message telebot.Message) {
	bot := registry.Bot

	techExp := regexp.MustCompile(`(?i)^\!ттх$`)
	questionExp := regexp.MustCompile(`^.*(gooby|губи|губ(я)+н).*\?$`)
	highlightedExp := regexp.MustCompile(`^.*(gooby|губи|губ(я)+н).*$`)

	switch {
		case techExp.MatchString(message.Text):
			bot.SendMessage(message.Chat,
						"ТТХ: https://drive.google.com/open?id=139ZWbP-CAV_u5nzQ6skbHRjb7eofzfdh8eA4_q7McFM",
						&telebot.SendOptions{DisableWebPagePreview: true, DisableNotification: true})

		case questionExp.MatchString(message.Text):
			var replyText string

			rngInt := rng.Int()

			switch {
				case rngInt % 100 == 0:
					replyText = "Заткнись, пидор"
				case rngInt % 2 == 0:
					replyText = "Да"
				default:
					replyText = "Нет"
			}

			bot.SendMessage(message.Chat, replyText, &telebot.SendOptions{ReplyTo: message})

		case highlightedExp.MatchString(message.Text):
			bot.SendMessage(message.Chat, "herp derp", nil)
	}
}

func (p *ReplyPlugin) Stop() {
}
<commit_msg>Fix incorrent indentation<commit_after>package reply

import (
	"regexp"
	"math/rand"
	"time"

	"github.com/tucnak/telebot"
	"github.com/asdine/storm"

	"github.com/focusshifter/muxgoob/registry"
)

type ReplyPlugin struct {
}

var db *storm.DB
var rng *rand.Rand

func init() {
	registry.RegisterPlugin(&ReplyPlugin{})
}

func (p *ReplyPlugin) Start(sharedDb *storm.DB) {
	db = sharedDb
	rng = rand.New(rand.NewSource(time.Now().UnixNano()))
}

func (p *ReplyPlugin) Run(message telebot.Message) {
	bot := registry.Bot

	techExp := regexp.MustCompile(`(?i)^\!ттх$`)
	questionExp := regexp.MustCompile(`^.*(gooby|губи|губ(я)+н).*\?$`)
	highlightedExp := regexp.MustCompile(`^.*(gooby|губи|губ(я)+н).*$`)

	switch {
		case techExp.MatchString(message.Text):
			bot.SendMessage(message.Chat,
						"ТТХ: https://drive.google.com/open?id=139ZWbP-CAV_u5nzQ6skbHRjb7eofzfdh8eA4_q7McFM",
						&telebot.SendOptions{DisableWebPagePreview: true, DisableNotification: true})

		case questionExp.MatchString(message.Text):
			var replyText string

			rngInt := rng.Int()

			switch {
				case rngInt % 100 == 0:
					replyText = "Заткнись, пидор"
				case rngInt % 2 == 0:
					replyText = "Да"
				default:
					replyText = "Нет"
			}

			bot.SendMessage(message.Chat, replyText, &telebot.SendOptions{ReplyTo: message})

		case highlightedExp.MatchString(message.Text):
			bot.SendMessage(message.Chat, "herp derp", nil)
	}
}

func (p *ReplyPlugin) Stop() {
}
<|endoftext|>


<commit_before>package stream

import (
	"net"

	"veyron.io/veyron/veyron2/naming"
	"veyron.io/veyron/veyron2/security"
)

// Flow is the interface for a flow-controlled channel multiplexed on a Virtual
// Circuit (VC) (and its underlying network connections).
//
// This allows for a single level of multiplexing and flow-control over
// multiple concurrent streams (that may be used for RPCs) over multiple
// VCs over a single underlying network connection.
type Flow interface {
	// Flow objects implement the net.Conn interface.
	net.Conn

	// Returns the local veyron Endpoint
	LocalEndpoint() naming.Endpoint

	// Returns the remote veyron Endpoint
	RemoteEndpoint() naming.Endpoint

	// Cancel, like Close, closes the Flow but unlike Close discards any queued writes.
	Cancel()

	// LocalID returns the identity of the local end of a Flow.
	LocalID() security.PublicID

	// RemoteID returns the identity of the remote end of a Flow.
	RemoteID() security.PublicID

	// Closed returns true if the flow has been closed or cancelled.
	IsClosed() bool

	// Closed returns a channel that remains open until the flow has been closed.
	Closed() <-chan struct{}
}

// FlowOpt is the interface for all Flow options.
type FlowOpt interface {
	IPCStreamFlowOpt()
}

// Listener is the interface for accepting Flows created by a remote process.
type Listener interface {
	// Accept blocks until a new Flow has been initiated by a remote process.
	// TODO(toddw): This should be:
	//   Accept() (Flow, Connector, error)
	Accept() (Flow, error)

	// Close prevents new Flows from being accepted on this Listener.
	// Previously accepted Flows are not closed down.
	Close() error
}

// ListenerOpt is the interface for all options that control the creation of a
// Listener.
type ListenerOpt interface {
	IPCStreamListenerOpt()
}

// Connector is the interface for initiating Flows to a remote process over a
// Virtual Circuit (VC).
type Connector interface {
	Connect(opts ...FlowOpt) (Flow, error)
}

// VC is the interface for creating authenticated and secure end-to-end
// streams.
//
// VCs are multiplexed onto underlying network conections and can span
// multiple hops. Authentication and encryption are end-to-end, even though
// underlying network connections span a single hop.
type VC interface {
	Connector
	Listen() (Listener, error)
}

// VCOpt is the interface for all VC options.
type VCOpt interface {
	IPCStreamVCOpt()
}

// Manager is the interface for managing the creation of VCs.
type Manager interface {
	// Listen creates a Listener that can be used to accept Flows initiated
	// with the provided network address.
	//
	// For example:
	//   ln, ep, err := Listen("tcp", ":0")
	//   for {
	//     flow, err := ln.Accept()
	//     // process flow
	//   }
	// can be used to accept Flows initiated by remote processes to the endpoint
	// identified by the returned Endpoint.
	//
	// Typical options accepted:
	// veyron2.TLSConfig
	// veyron2.ServerID
	Listen(protocol, address string, opts ...ListenerOpt) (Listener, naming.Endpoint, error)

	// Dial creates a VC to the provided remote endpoint.
	//
	// Typical options accepted:
	// TODO(ashankar): Update comment as these option types will change
	// once we finalize the "public" and "private" ids in veyron2.
	// veyron2.LocalID - Identity of the caller
	// veyron2.ServerID - Expected identity of the server
	// veyron2.TLSConfig
	//
	// TODO: Should any of these security related options be made explicit
	// positional arguments?
	Dial(remote naming.Endpoint, opts ...VCOpt) (VC, error)

	// ShutdownEndpoint closes all VCs (and Flows and Listeners over it)
	// involving the provided remote endpoint.
	ShutdownEndpoint(remote naming.Endpoint)

	// Shutdown closes all VCs and Listeners (and Flows over them) and
	// frees up internal data structures.
	// The Manager is not usable after Shutdown has been called.
	Shutdown()

	// RoutingID returns the Routing ID associated with the VC.
	RoutingID() naming.RoutingID
}

// ManagerOpt is the interface for all Manager related options provided to Runtime.NewStreamManager.
type ManagerOpt interface {
	IPCStreamManagerOpt()
}
<commit_msg>veyron/runtimes/google/ipc/stream: Support for the new security model primitives.<commit_after>package stream

import (
	"net"

	"veyron.io/veyron/veyron2/naming"
	"veyron.io/veyron/veyron2/security"
)

// Flow is the interface for a flow-controlled channel multiplexed on a Virtual
// Circuit (VC) (and its underlying network connections).
//
// This allows for a single level of multiplexing and flow-control over
// multiple concurrent streams (that may be used for RPCs) over multiple
// VCs over a single underlying network connection.
type Flow interface {
	// Flow objects implement the net.Conn interface.
	net.Conn
	// LocalEndpoint returns the local veyron Endpoint
	LocalEndpoint() naming.Endpoint
	// RemoteEndpoint returns the remote veyron Endpoint
	RemoteEndpoint() naming.Endpoint
	// LocalPrincipal returns the Principal at the local end of the flow that has authenticated with the remote end.
	LocalPrincipal() security.Principal
	// LocalBlessings returns the blessings presented by the local end of the flow during authentication.
	LocalBlessings() security.Blessings
	// RemoteBlessings returns the blessings presented by the remote end of the flow during authentication.
	RemoteBlessings() security.Blessings
	// Cancel, like Close, closes the Flow but unlike Close discards any queued writes.
	Cancel()
	// Closed returns true if the flow has been closed or cancelled.
	IsClosed() bool
	// Closed returns a channel that remains open until the flow has been closed.
	Closed() <-chan struct{}
	// TODO(ashankar): Remove both of these once the new security API transition is complete.
	LocalID() security.PublicID
	RemoteID() security.PublicID
}

// FlowOpt is the interface for all Flow options.
type FlowOpt interface {
	IPCStreamFlowOpt()
}

// Listener is the interface for accepting Flows created by a remote process.
type Listener interface {
	// Accept blocks until a new Flow has been initiated by a remote process.
	// TODO(toddw): This should be:
	//   Accept() (Flow, Connector, error)
	Accept() (Flow, error)

	// Close prevents new Flows from being accepted on this Listener.
	// Previously accepted Flows are not closed down.
	Close() error
}

// ListenerOpt is the interface for all options that control the creation of a
// Listener.
type ListenerOpt interface {
	IPCStreamListenerOpt()
}

// Connector is the interface for initiating Flows to a remote process over a
// Virtual Circuit (VC).
type Connector interface {
	Connect(opts ...FlowOpt) (Flow, error)
}

// VC is the interface for creating authenticated and secure end-to-end
// streams.
//
// VCs are multiplexed onto underlying network conections and can span
// multiple hops. Authentication and encryption are end-to-end, even though
// underlying network connections span a single hop.
type VC interface {
	Connector
	Listen() (Listener, error)
}

// VCOpt is the interface for all VC options.
type VCOpt interface {
	IPCStreamVCOpt()
}

// Manager is the interface for managing the creation of VCs.
type Manager interface {
	// Listen creates a Listener that can be used to accept Flows initiated
	// with the provided network address.
	//
	// For example:
	//   ln, ep, err := Listen("tcp", ":0")
	//   for {
	//     flow, err := ln.Accept()
	//     // process flow
	//   }
	// can be used to accept Flows initiated by remote processes to the endpoint
	// identified by the returned Endpoint.
	//
	// Typical options accepted:
	// veyron2.TLSConfig
	// veyron2.ServerID
	Listen(protocol, address string, opts ...ListenerOpt) (Listener, naming.Endpoint, error)

	// Dial creates a VC to the provided remote endpoint.
	//
	// Typical options accepted:
	// TODO(ashankar): Update comment as these option types will change
	// once we finalize the "public" and "private" ids in veyron2.
	// veyron2.LocalID - Identity of the caller
	// veyron2.ServerID - Expected identity of the server
	// veyron2.TLSConfig
	//
	// TODO: Should any of these security related options be made explicit
	// positional arguments?
	Dial(remote naming.Endpoint, opts ...VCOpt) (VC, error)

	// ShutdownEndpoint closes all VCs (and Flows and Listeners over it)
	// involving the provided remote endpoint.
	ShutdownEndpoint(remote naming.Endpoint)

	// Shutdown closes all VCs and Listeners (and Flows over them) and
	// frees up internal data structures.
	// The Manager is not usable after Shutdown has been called.
	Shutdown()

	// RoutingID returns the Routing ID associated with the VC.
	RoutingID() naming.RoutingID
}

// ManagerOpt is the interface for all Manager related options provided to Runtime.NewStreamManager.
type ManagerOpt interface {
	IPCStreamManagerOpt()
}
<|endoftext|>


<commit_before>package adngo

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"log"
	"net/http"
	"net/url"
	"strings"
)

// Our API Urls
const (
	baseURI = "https://alpha-api.app.net/"
	authURI = "https://account.app.net/oauth/"
)

// This is our scopes struct to check for that.
type Scopes []string

func (s Scopes) Spaced() string {
	return strings.Join(s, " ")
}

func (s Scopes) String() string {
	return strings.Join(s, ",")
}

type App struct {
	clientId     string
	clientSecret string
	accessToken  string
	RedirectURI  string
	Scopes       Scopes
}

var httpClient = &http.Client{}

func (a *App) do(method, url, bodyType string, data url.Values) (resp *http.Response, err error) {
	req, err := http.NewRequest(method, url, nil)
	if err != nil {
		log.Fatal(err)
	}

	if data != nil {
		req.Body = bytes.NewBufferString(data.Encode())
	}
	if a.accessToken != "" {
		req.Header.Add("Authorization", "Bearer "+a.accessToken)
	}
	if bodyType != "" {
		req.Header.Add("Content-Type", bodyType)
	}

	return httpClient.Do(req)
}

func (a *App) get(url, bodyType string) (resp *http.Response, err error) {
	return a.do("GET", url, bodyType, nil)
}

func (a *App) post(url string, bodyType string, data url.Values) (resp *http.Response, err error) {
	return a.do("POST", url, bodyType, data)
}

func (a *App) put(url string, bodyType string, data url.Values) (resp *http.Response, err error) {
	return a.do("PUT", url, bodyType, data)
}

func (a *App) patch(url string, bodyType string, data url.Values) (resp *http.Response, err error) {
	return a.do("PATCH", url, bodyType, data)
}

func (a *App) delete(url string) (resp *http.Response, err error) {
	return a.do("DELETE", url, "application/json", nil)
}

// Do we even need this??
func (a *App) VerifyToken(delegate bool) {
	if delegate {
		auth := []byte(a.clientId + ":" + a.clientSecret)
		req := http.NewRequest("GET", baseURI+"stream/0/token", nil)
		req.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString(auth))
		req.Header.Add("Identity-Delegate-Token", "True")

		resp, err := httpClient.Do(req)
	} else {
		resp, err := a.get(baseURI+"stream/0/token", "application/json")
	}
}

func (a *App) AuthURI(clientSide, appStore bool) {
	data := url.Values{}
	data.Add("client_id", a.clientId)
	data.Add("redirect_uri", a.RedirectURI)
	data.Add("scope", a.Scopes.String())

	if clientSide {
		data.Add("response_type", "token")
	} else {
		data.Add("response_type", "code")
	}
	if appStore {
		data.Add("adnview", "appstore")
	}

	return authURI + "authenticate?" + data.Encode()
}

func (a *App) GetAccessToken(code string, app bool) {
	if app {
		data := url.Values{}
		data.Add("client_id", a.clientId)
		data.Add("client_secret", a.clientSecret)
		data.Add("grant_type", "client_credentials")

		resp, err := a.post(authURI+"access_token", "", data)
	}
}

func (a *App) ProcessText(text string) {
	data := url.Values{}
	data.Add("text", text)

	resp, err := a.post(baseURI+"stream/0/text/process", "", data)
}

// Retrieves the App.Net Configuration Object
func (a *App) GetConfig() (config interface{}) {
	resp, err := a.get(baseURI+"stream/0/config", "application/json")
	if err != nil {
		log.Fatal(err)
	}

	var conf interface{}
	err = json.Unmarshal(resp, &config)
	if err != nil {
		log.Fatal(err)
	}

	return conf
}
<commit_msg>Yet More Fixes.<commit_after>package adngo

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"log"
	"net/http"
	"net/url"
	"strings"
)

// Our API Urls
const (
	baseURI = "https://alpha-api.app.net/"
	authURI = "https://account.app.net/oauth/"
)

// This is our scopes struct to check for that.
type Scopes []string

func (s Scopes) Spaced() string {
	return strings.Join(s, " ")
}

func (s Scopes) String() string {
	return strings.Join(s, ",")
}

// A custom type that satisfies the io.ReadCloser needed by the http Request
type dataCloser struct {
	io.Reader
}

func (dataCloser) Close() os.Error { return nil }

// Our primary API struct. It's the source of all our awesome.
type App struct {
	clientId     string
	clientSecret string
	accessToken  string
	RedirectURI  string
	Scopes       Scopes
}

var httpClient = &http.Client{}

func (a *App) do(method, url, bodyType string, data url.Values) (resp *http.Response, err error) {
	req, err := http.NewRequest(method, url, nil)
	if err != nil {
		log.Fatal(err)
	}

	if data != nil {
		req.Body = dataCloser{bytes.NewBufferString(data.Encode())}
	}
	if a.accessToken != "" {
		req.Header.Add("Authorization", "Bearer "+a.accessToken)
	}
	if bodyType != "" {
		req.Header.Add("Content-Type", bodyType)
	}

	return httpClient.Do(req)
}

func (a *App) get(url, bodyType string) (resp *http.Response, err error) {
	return a.do("GET", url, bodyType, nil)
}

func (a *App) post(url string, bodyType string, data url.Values) (resp *http.Response, err error) {
	return a.do("POST", url, bodyType, data)
}

func (a *App) put(url string, bodyType string, data url.Values) (resp *http.Response, err error) {
	return a.do("PUT", url, bodyType, data)
}

func (a *App) patch(url string, bodyType string, data url.Values) (resp *http.Response, err error) {
	return a.do("PATCH", url, bodyType, data)
}

func (a *App) delete(url string) (resp *http.Response, err error) {
	return a.do("DELETE", url, "application/json", nil)
}

// Do we even need this??
func (a *App) VerifyToken(delegate bool) {
	if delegate {
		auth := []byte(a.clientId + ":" + a.clientSecret)
		req := http.NewRequest("GET", baseURI+"stream/0/token", nil)
		req.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString(auth))
		req.Header.Add("Identity-Delegate-Token", "True")

		resp, err := httpClient.Do(req)
	} else {
		resp, err := a.get(baseURI+"stream/0/token", "application/json")
	}
}

func (a *App) AuthURI(clientSide, appStore bool) (url string) {
	data := url.Values{}
	data.Add("client_id", a.clientId)
	data.Add("redirect_uri", a.RedirectURI)
	data.Add("scope", a.Scopes.String())

	if clientSide {
		data.Add("response_type", "token")
	} else {
		data.Add("response_type", "code")
	}
	if appStore {
		data.Add("adnview", "appstore")
	}

	return authURI + "authenticate?" + data.Encode()
}

func (a *App) GetAccessToken(code string, app bool) {
	if app {
		data := url.Values{}
		data.Add("client_id", a.clientId)
		data.Add("client_secret", a.clientSecret)
		data.Add("grant_type", "client_credentials")

		resp, err := a.post(authURI+"access_token", "", data)
	}
}

func (a *App) ProcessText(text string) {
	data := url.Values{}
	data.Add("text", text)

	resp, err := a.post(baseURI+"stream/0/text/process", "", data)
}

// Retrieves the App.Net Configuration Object
func (a *App) GetConfig() (config interface{}) {
	resp, err := a.get(baseURI+"stream/0/config", "application/json")
	if err != nil {
		log.Fatal(err)
	}

	var conf interface{}
	err = json.Unmarshal(resp.Body, &config)
	if err != nil {
		log.Fatal(err)
	}

	return conf
}
<|endoftext|>