<commit_before>// +build go1.8

package well

import (
	"context"
	"crypto/tls"
	"io"
	"net"
	"net/http"
	"net/url"
	"os"
	"sync"
	"time"

	"github.com/cybozu-go/log"
	"github.com/cybozu-go/netutil"
)

const (
	defaultHTTPReadTimeout = 30 * time.Second

	// request tracking header.
	defaultRequestIDHeader = "X-Cybozu-Request-ID"

	requestIDHeaderEnv = "REQUEST_ID_HEADER"
)

var (
	requestIDHeader = defaultRequestIDHeader
)

func init() {
	hn := os.Getenv(requestIDHeaderEnv)
	if len(hn) > 0 {
		requestIDHeader = hn
	}
}

// HTTPServer is a wrapper for http.Server.
//
// This struct overrides Serve and ListenAndServe* methods.
//
// http.Server members are replaced as following:
//    - Handler is replaced with a wrapper handler that logs requests.
//    - ReadTimeout is set to 30 seconds if it is zero.
//    - ConnState is replaced with the one provided by the framework.
type HTTPServer struct {
	*http.Server

	// AccessLog is a logger for access logs.
	// If this is nil, the default logger is used.
	AccessLog *log.Logger

	// ShutdownTimeout is the maximum duration the server waits for
	// all connections to be closed before shutdown.
	//
	// Zero duration disables timeout.
	ShutdownTimeout time.Duration

	// Env is the environment where this server runs.
	//
	// The global environment is used if Env is nil.
	Env *Environment

	handler     http.Handler
	shutdownErr error
	generator   *IDGenerator

	initOnce sync.Once
}

// StdResponseWriter is the interface implemented by
// the ResponseWriter from http.Server for non-HTTP/2 requests.
//
// HTTPServer's ResponseWriter implements this as well.
type StdResponseWriter interface {
	http.ResponseWriter
	io.ReaderFrom
	http.Flusher
	http.CloseNotifier
	http.Hijacker
	WriteString(data string) (int, error)
}

// StdResponseWriter2 is the interface implemented by
// the ResponseWriter from http.Server for HTTP/2 requests.
//
// HTTPServer's ResponseWriter implements this as well.
type StdResponseWriter2 interface {
	http.ResponseWriter
	http.Flusher
	http.CloseNotifier
	http.Pusher
	WriteString(data string) (int, error)
}

type logWriter interface {
	Status() int
	Size() int64
}

type logResponseWriter struct {
	StdResponseWriter
	status int
	size   int64
}

func (w *logResponseWriter) WriteHeader(status int) {
	w.status = status
	w.StdResponseWriter.WriteHeader(status)
}

func (w *logResponseWriter) Write(data []byte) (int, error) {
	n, err := w.StdResponseWriter.Write(data)
	w.size += int64(n)
	return n, err
}

func (w *logResponseWriter) ReadFrom(r io.Reader) (int64, error) {
	n, err := w.StdResponseWriter.ReadFrom(r)
	w.size += n
	return n, err
}

func (w *logResponseWriter) WriteString(data string) (int, error) {
	n, err := w.StdResponseWriter.WriteString(data)
	w.size += int64(n)
	return n, err
}

func (w *logResponseWriter) Status() int {
	return w.status
}

func (w *logResponseWriter) Size() int64 {
	return w.size
}

type logResponseWriter2 struct {
	StdResponseWriter2
	status int
	size   int64
}

func (w *logResponseWriter2) WriteHeader(status int) {
	w.status = status
	w.StdResponseWriter2.WriteHeader(status)
}

func (w *logResponseWriter2) Write(data []byte) (int, error) {
	n, err := w.StdResponseWriter2.Write(data)
	w.size += int64(n)
	return n, err
}

func (w *logResponseWriter2) WriteString(data string) (int, error) {
	n, err := w.StdResponseWriter2.WriteString(data)
	w.size += int64(n)
	return n, err
}

func (w *logResponseWriter2) Status() int {
	return w.status
}

func (w *logResponseWriter2) Size() int64 {
	return w.size
}

func createLogWriter(w http.ResponseWriter) (http.ResponseWriter, logWriter) {
	if srw1, ok := w.(StdResponseWriter); ok {
		t := &logResponseWriter{srw1, http.StatusOK, 0}
		return t, t
	}

	if srw2, ok := w.(StdResponseWriter2); ok {
		t := &logResponseWriter2{srw2, http.StatusOK, 0}
		return t, t
	}

	panic("unexpected ResponseWriter implementation")
}

// ServeHTTP implements http.Handler interface.
func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	startTime := time.Now()

	w, lw := createLogWriter(w)

	ctx, cancel := context.WithCancel(s.Env.ctx)
	defer cancel()

	reqid := r.Header.Get(requestIDHeader)
	if len(reqid) == 0 {
		reqid = s.generator.Generate()
	}
	ctx = WithRequestID(ctx, reqid)

	s.handler.ServeHTTP(w, r.WithContext(ctx))
	status := lw.Status()

	fields := map[string]interface{}{
		log.FnType:           "access",
		log.FnResponseTime:   time.Since(startTime).Seconds(),
		log.FnProtocol:       r.Proto,
		log.FnHTTPStatusCode: status,
		log.FnHTTPMethod:     r.Method,
		log.FnURL:            r.RequestURI,
		log.FnHTTPHost:       r.Host,
		log.FnRequestSize:    r.ContentLength,
		log.FnResponseSize:   lw.Size(),
	}
	ip, _, err := net.SplitHostPort(r.RemoteAddr)
	if err == nil {
		fields[log.FnRemoteAddress] = ip
	}
	ua := r.Header.Get("User-Agent")
	if len(ua) > 0 {
		fields[log.FnHTTPUserAgent] = ua
	}
	if len(reqid) > 0 {
		fields[log.FnRequestID] = reqid
	}

	lv := log.LvInfo
	switch {
	case 500 <= status:
		lv = log.LvError
	case 400 <= status:
		lv = log.LvWarn
	}
	s.AccessLog.Log(lv, "well: access", fields)
}

func (s *HTTPServer) init() {
	s.generator = NewIDGenerator()

	if s.Server.Handler == nil {
		panic("Handler must not be nil")
	}
	s.handler = s.Server.Handler
	s.Server.Handler = s
	if s.Server.ReadTimeout == 0 {
		s.Server.ReadTimeout = defaultHTTPReadTimeout
	}

	if s.AccessLog == nil {
		s.AccessLog = log.DefaultLogger()
	}

	if s.Env == nil {
		s.Env = defaultEnv
	}

	s.Env.Go(s.wait)
}

func (s *HTTPServer) wait(ctx context.Context) error {
	<-ctx.Done()

	s.Server.SetKeepAlivesEnabled(false)

	ctx = context.Background()
	if s.ShutdownTimeout != 0 {
		ctx2, cancel := context.WithTimeout(ctx, s.ShutdownTimeout)
		defer cancel()
		ctx = ctx2
	}

	err := s.Server.Shutdown(ctx)
	if err != nil {
		log.Warn("well: unclean shutdown", map[string]interface{}{
			log.FnError: err,
		})
		s.shutdownErr = err
	}
	return err
}

// TimedOut returns true if the server shut down before all connections
// got closed.
func (s *HTTPServer) TimedOut() bool {
	return s.shutdownErr == context.DeadlineExceeded
}

// Serve overrides http.Server's Serve method.
//
// Unlike the original, this method returns immediately just after
// starting a goroutine to accept connections.
//
// The framework automatically closes l when the environment's Cancel
// is called.
//
// Serve always returns nil.
func (s *HTTPServer) Serve(l net.Listener) error {
	s.initOnce.Do(s.init)

	l = netutil.KeepAliveListener(l)

	go func() {
		s.Server.Serve(l)
	}()

	return nil
}

// ListenAndServe overrides http.Server's method.
//
// Unlike the original, this method returns immediately just after
// starting a goroutine to accept connections.  To stop listening,
// call the environment's Cancel.
//
// ListenAndServe returns non-nil error if and only if net.Listen failed.
func (s *HTTPServer) ListenAndServe() error {
	addr := s.Server.Addr
	if addr == "" {
		addr = ":http"
	}
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return s.Serve(ln)
}

// ListenAndServeTLS overrides http.Server's method.
//
// Unlike the original, this method returns immediately just after
// starting a goroutine to accept connections.  To stop listening,
// call the environment's Cancel.
//
// Another difference from the original is that certFile and keyFile
// must be specified.  If not, configure http.Server.TLSConfig
// manually and use Serve().
//
// HTTP/2 is always enabled.
//
// ListenAndServeTLS returns non-nil error if net.Listen failed
// or failed to load certificate files.
func (s *HTTPServer) ListenAndServeTLS(certFile, keyFile string) error {
	addr := s.Server.Addr
	if addr == "" {
		addr = ":https"
	}

	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}

	config := &tls.Config{
		NextProtos:               []string{"h2", "http/1.1"},
		Certificates:             []tls.Certificate{cert},
		PreferServerCipherSuites: true,
		ClientSessionCache:       tls.NewLRUClientSessionCache(0),
	}
	s.Server.TLSConfig = config

	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}

	tlsListener := tls.NewListener(ln, config)
	return s.Serve(tlsListener)
}

// HTTPClient is a thin wrapper for *http.Client.
//
// This overrides Do method to add the request tracking header if
// the passed request's context brings a request tracking ID.  Do
// also records the request log to Logger.
//
// Do not use Get/Head/Post/PostForm.  They panics.
type HTTPClient struct {
	*http.Client

	// Severity is used to log successful requests.
	//
	// Zero suppresses logging.  Valid values are one of
	// log.LvDebug, log.LvInfo, and so on.
	//
	// Errors are always logged with log.LvError.
	Severity int

	// Logger for HTTP request.  If nil, the default logger is used.
	Logger *log.Logger
}

// Do overrides http.Client.Do.
//
// req's context should have been set by http.Request.WithContext
// for request tracking and context-based cancelation.
func (c *HTTPClient) Do(req *http.Request) (*http.Response, error) {
	ctx := req.Context()
	v := ctx.Value(RequestIDContextKey)
	if v != nil {
		req.Header.Set(requestIDHeader, v.(string))
	}
	st := time.Now()
	resp, err := c.Client.Do(req)

	logger := c.Logger
	if logger == nil {
		logger = log.DefaultLogger()
	}

	if err == nil && (c.Severity == 0 || !logger.Enabled(c.Severity)) {
		// successful logs are suppressed if c.Severity is 0 or
		// logger threshold is under c.Severity.
		return resp, err
	}

	fields := FieldsFromContext(ctx)
	fields[log.FnType] = "http"
	fields[log.FnResponseTime] = time.Since(st).Seconds()
	fields[log.FnHTTPMethod] = req.Method
	fields[log.FnURL] = req.URL.String()
	fields[log.FnStartAt] = st

	if err != nil {
		fields["error"] = err.Error()
		logger.Error("well: http", fields)
		return resp, err
	}

	fields[log.FnHTTPStatusCode] = resp.StatusCode
	logger.Log(c.Severity, "well: http", fields)
	return resp, err
}

// Get panics.
func (c *HTTPClient) Get(url string) (*http.Response, error) {
	panic("Use Do")
}

// Head panics.
func (c *HTTPClient) Head(url string) (*http.Response, error) {
	panic("Use Do")
}

// Post panics.
func (c *HTTPClient) Post(url, bodyType string, body io.Reader) (*http.Response, error) {
	panic("Use Do")
}

// PostForm panics.
func (c *HTTPClient) PostForm(url string, data url.Values) (*http.Response, error) {
	panic("Use Do")
}
<commit_msg>add exported getter for requestIDHeader<commit_after>// +build go1.8

package well

import (
	"context"
	"crypto/tls"
	"io"
	"net"
	"net/http"
	"net/url"
	"os"
	"sync"
	"time"

	"github.com/cybozu-go/log"
	"github.com/cybozu-go/netutil"
)

const (
	defaultHTTPReadTimeout = 30 * time.Second

	// request tracking header.
	defaultRequestIDHeader = "X-Cybozu-Request-ID"

	requestIDHeaderEnv = "REQUEST_ID_HEADER"
)

var (
	requestIDHeader = defaultRequestIDHeader
)

func init() {
	hn := os.Getenv(requestIDHeaderEnv)
	if len(hn) > 0 {
		requestIDHeader = hn
	}
}

// RequestIDHeader provides the name of the request tracking header
func RequestIDHeader() string {
	return requestIDHeader
}

// HTTPServer is a wrapper for http.Server.
//
// This struct overrides Serve and ListenAndServe* methods.
//
// http.Server members are replaced as following:
//    - Handler is replaced with a wrapper handler that logs requests.
//    - ReadTimeout is set to 30 seconds if it is zero.
//    - ConnState is replaced with the one provided by the framework.
type HTTPServer struct {
	*http.Server

	// AccessLog is a logger for access logs.
	// If this is nil, the default logger is used.
	AccessLog *log.Logger

	// ShutdownTimeout is the maximum duration the server waits for
	// all connections to be closed before shutdown.
	//
	// Zero duration disables timeout.
	ShutdownTimeout time.Duration

	// Env is the environment where this server runs.
	//
	// The global environment is used if Env is nil.
	Env *Environment

	handler     http.Handler
	shutdownErr error
	generator   *IDGenerator

	initOnce sync.Once
}

// StdResponseWriter is the interface implemented by
// the ResponseWriter from http.Server for non-HTTP/2 requests.
//
// HTTPServer's ResponseWriter implements this as well.
type StdResponseWriter interface {
	http.ResponseWriter
	io.ReaderFrom
	http.Flusher
	http.CloseNotifier
	http.Hijacker
	WriteString(data string) (int, error)
}

// StdResponseWriter2 is the interface implemented by
// the ResponseWriter from http.Server for HTTP/2 requests.
//
// HTTPServer's ResponseWriter implements this as well.
type StdResponseWriter2 interface {
	http.ResponseWriter
	http.Flusher
	http.CloseNotifier
	http.Pusher
	WriteString(data string) (int, error)
}

type logWriter interface {
	Status() int
	Size() int64
}

type logResponseWriter struct {
	StdResponseWriter
	status int
	size   int64
}

func (w *logResponseWriter) WriteHeader(status int) {
	w.status = status
	w.StdResponseWriter.WriteHeader(status)
}

func (w *logResponseWriter) Write(data []byte) (int, error) {
	n, err := w.StdResponseWriter.Write(data)
	w.size += int64(n)
	return n, err
}

func (w *logResponseWriter) ReadFrom(r io.Reader) (int64, error) {
	n, err := w.StdResponseWriter.ReadFrom(r)
	w.size += n
	return n, err
}

func (w *logResponseWriter) WriteString(data string) (int, error) {
	n, err := w.StdResponseWriter.WriteString(data)
	w.size += int64(n)
	return n, err
}

func (w *logResponseWriter) Status() int {
	return w.status
}

func (w *logResponseWriter) Size() int64 {
	return w.size
}

type logResponseWriter2 struct {
	StdResponseWriter2
	status int
	size   int64
}

func (w *logResponseWriter2) WriteHeader(status int) {
	w.status = status
	w.StdResponseWriter2.WriteHeader(status)
}

func (w *logResponseWriter2) Write(data []byte) (int, error) {
	n, err := w.StdResponseWriter2.Write(data)
	w.size += int64(n)
	return n, err
}

func (w *logResponseWriter2) WriteString(data string) (int, error) {
	n, err := w.StdResponseWriter2.WriteString(data)
	w.size += int64(n)
	return n, err
}

func (w *logResponseWriter2) Status() int {
	return w.status
}

func (w *logResponseWriter2) Size() int64 {
	return w.size
}

func createLogWriter(w http.ResponseWriter) (http.ResponseWriter, logWriter) {
	if srw1, ok := w.(StdResponseWriter); ok {
		t := &logResponseWriter{srw1, http.StatusOK, 0}
		return t, t
	}

	if srw2, ok := w.(StdResponseWriter2); ok {
		t := &logResponseWriter2{srw2, http.StatusOK, 0}
		return t, t
	}

	panic("unexpected ResponseWriter implementation")
}

// ServeHTTP implements http.Handler interface.
func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	startTime := time.Now()

	w, lw := createLogWriter(w)

	ctx, cancel := context.WithCancel(s.Env.ctx)
	defer cancel()

	reqid := r.Header.Get(requestIDHeader)
	if len(reqid) == 0 {
		reqid = s.generator.Generate()
	}
	ctx = WithRequestID(ctx, reqid)

	s.handler.ServeHTTP(w, r.WithContext(ctx))
	status := lw.Status()

	fields := map[string]interface{}{
		log.FnType:           "access",
		log.FnResponseTime:   time.Since(startTime).Seconds(),
		log.FnProtocol:       r.Proto,
		log.FnHTTPStatusCode: status,
		log.FnHTTPMethod:     r.Method,
		log.FnURL:            r.RequestURI,
		log.FnHTTPHost:       r.Host,
		log.FnRequestSize:    r.ContentLength,
		log.FnResponseSize:   lw.Size(),
	}
	ip, _, err := net.SplitHostPort(r.RemoteAddr)
	if err == nil {
		fields[log.FnRemoteAddress] = ip
	}
	ua := r.Header.Get("User-Agent")
	if len(ua) > 0 {
		fields[log.FnHTTPUserAgent] = ua
	}
	if len(reqid) > 0 {
		fields[log.FnRequestID] = reqid
	}

	lv := log.LvInfo
	switch {
	case 500 <= status:
		lv = log.LvError
	case 400 <= status:
		lv = log.LvWarn
	}
	s.AccessLog.Log(lv, "well: access", fields)
}

func (s *HTTPServer) init() {
	s.generator = NewIDGenerator()

	if s.Server.Handler == nil {
		panic("Handler must not be nil")
	}
	s.handler = s.Server.Handler
	s.Server.Handler = s
	if s.Server.ReadTimeout == 0 {
		s.Server.ReadTimeout = defaultHTTPReadTimeout
	}

	if s.AccessLog == nil {
		s.AccessLog = log.DefaultLogger()
	}

	if s.Env == nil {
		s.Env = defaultEnv
	}

	s.Env.Go(s.wait)
}

func (s *HTTPServer) wait(ctx context.Context) error {
	<-ctx.Done()

	s.Server.SetKeepAlivesEnabled(false)

	ctx = context.Background()
	if s.ShutdownTimeout != 0 {
		ctx2, cancel := context.WithTimeout(ctx, s.ShutdownTimeout)
		defer cancel()
		ctx = ctx2
	}

	err := s.Server.Shutdown(ctx)
	if err != nil {
		log.Warn("well: unclean shutdown", map[string]interface{}{
			log.FnError: err,
		})
		s.shutdownErr = err
	}
	return err
}

// TimedOut returns true if the server shut down before all connections
// got closed.
func (s *HTTPServer) TimedOut() bool {
	return s.shutdownErr == context.DeadlineExceeded
}

// Serve overrides http.Server's Serve method.
//
// Unlike the original, this method returns immediately just after
// starting a goroutine to accept connections.
//
// The framework automatically closes l when the environment's Cancel
// is called.
//
// Serve always returns nil.
func (s *HTTPServer) Serve(l net.Listener) error {
	s.initOnce.Do(s.init)

	l = netutil.KeepAliveListener(l)

	go func() {
		s.Server.Serve(l)
	}()

	return nil
}

// ListenAndServe overrides http.Server's method.
//
// Unlike the original, this method returns immediately just after
// starting a goroutine to accept connections.  To stop listening,
// call the environment's Cancel.
//
// ListenAndServe returns non-nil error if and only if net.Listen failed.
func (s *HTTPServer) ListenAndServe() error {
	addr := s.Server.Addr
	if addr == "" {
		addr = ":http"
	}
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return s.Serve(ln)
}

// ListenAndServeTLS overrides http.Server's method.
//
// Unlike the original, this method returns immediately just after
// starting a goroutine to accept connections.  To stop listening,
// call the environment's Cancel.
//
// Another difference from the original is that certFile and keyFile
// must be specified.  If not, configure http.Server.TLSConfig
// manually and use Serve().
//
// HTTP/2 is always enabled.
//
// ListenAndServeTLS returns non-nil error if net.Listen failed
// or failed to load certificate files.
func (s *HTTPServer) ListenAndServeTLS(certFile, keyFile string) error {
	addr := s.Server.Addr
	if addr == "" {
		addr = ":https"
	}

	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}

	config := &tls.Config{
		NextProtos:               []string{"h2", "http/1.1"},
		Certificates:             []tls.Certificate{cert},
		PreferServerCipherSuites: true,
		ClientSessionCache:       tls.NewLRUClientSessionCache(0),
	}
	s.Server.TLSConfig = config

	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}

	tlsListener := tls.NewListener(ln, config)
	return s.Serve(tlsListener)
}

// HTTPClient is a thin wrapper for *http.Client.
//
// This overrides Do method to add the request tracking header if
// the passed request's context brings a request tracking ID.  Do
// also records the request log to Logger.
//
// Do not use Get/Head/Post/PostForm.  They panics.
type HTTPClient struct {
	*http.Client

	// Severity is used to log successful requests.
	//
	// Zero suppresses logging.  Valid values are one of
	// log.LvDebug, log.LvInfo, and so on.
	//
	// Errors are always logged with log.LvError.
	Severity int

	// Logger for HTTP request.  If nil, the default logger is used.
	Logger *log.Logger
}

// Do overrides http.Client.Do.
//
// req's context should have been set by http.Request.WithContext
// for request tracking and context-based cancelation.
func (c *HTTPClient) Do(req *http.Request) (*http.Response, error) {
	ctx := req.Context()
	v := ctx.Value(RequestIDContextKey)
	if v != nil {
		req.Header.Set(requestIDHeader, v.(string))
	}
	st := time.Now()
	resp, err := c.Client.Do(req)

	logger := c.Logger
	if logger == nil {
		logger = log.DefaultLogger()
	}

	if err == nil && (c.Severity == 0 || !logger.Enabled(c.Severity)) {
		// successful logs are suppressed if c.Severity is 0 or
		// logger threshold is under c.Severity.
		return resp, err
	}

	fields := FieldsFromContext(ctx)
	fields[log.FnType] = "http"
	fields[log.FnResponseTime] = time.Since(st).Seconds()
	fields[log.FnHTTPMethod] = req.Method
	fields[log.FnURL] = req.URL.String()
	fields[log.FnStartAt] = st

	if err != nil {
		fields["error"] = err.Error()
		logger.Error("well: http", fields)
		return resp, err
	}

	fields[log.FnHTTPStatusCode] = resp.StatusCode
	logger.Log(c.Severity, "well: http", fields)
	return resp, err
}

// Get panics.
func (c *HTTPClient) Get(url string) (*http.Response, error) {
	panic("Use Do")
}

// Head panics.
func (c *HTTPClient) Head(url string) (*http.Response, error) {
	panic("Use Do")
}

// Post panics.
func (c *HTTPClient) Post(url, bodyType string, body io.Reader) (*http.Response, error) {
	panic("Use Do")
}

// PostForm panics.
func (c *HTTPClient) PostForm(url string, data url.Values) (*http.Response, error) {
	panic("Use Do")
}
<|endoftext|>


<commit_before>package fasthttp

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"sync"
)

// Request represents HTTP request.
//
// It is forbidden copying Request instances. Create new instances
// and use CopyTo() instead.
type Request struct {
	// Request header
	Header RequestHeader

	// Request body.
	Body []byte

	// Request URI.
	// URI becomes available only after Request.ParseURI() call.
	URI       URI
	parsedURI bool

	// Arguments sent in POST.
	// PostArgs becomes available only after Request.ParsePostArgs() call.
	PostArgs       Args
	parsedPostArgs bool
}

// Response represents HTTP response.
//
// It is forbidden copying Response instances. Create new instances
// and use CopyTo() instead.
type Response struct {
	// Response header
	Header ResponseHeader

	// Response body.
	//
	// Either Body or BodyStream may be set, but not both.
	Body []byte

	// Response body stream.
	//
	// BodyStream may be set instead of Body for performance reasons only.
	//
	// BodyStream is read by Response.Write() until one of the following
	// events occur:
	// - Response.ContentLength bytes read if it is greater than 0.
	// - error or io.EOF is returned from BodyStream if Response.ContentLength
	// is 0 or negative.
	//
	// Response.Write() calls BodyStream.Close() (io.Closer) if such method
	// is present after finishing reading BodyStream.
	//
	// Either BodyStream or Body may be set, but not both.
	//
	// Response.Read() never sets BodyStream - it sets only Body.
	BodyStream io.Reader

	// If set to true, Response.Read() skips reading body.
	// Use it for HEAD requests.
	SkipBody bool
}

// CopyTo copies req contents to dst.
func (req *Request) CopyTo(dst *Request) {
	dst.Clear()
	req.Header.CopyTo(&dst.Header)
	dst.Body = append(dst.Body[:0], req.Body...)
	if req.parsedURI {
		dst.ParseURI()
	}
	if req.parsedPostArgs {
		dst.ParsePostArgs()
	}
}

// CopyTo copies resp contents to dst except of BodyStream.
func (resp *Response) CopyTo(dst *Response) {
	dst.Clear()
	resp.Header.CopyTo(&dst.Header)
	dst.Body = append(dst.Body[:0], resp.Body...)
	dst.SkipBody = resp.SkipBody
}

// ParseURI parses request uri and fills Request.URI.
func (req *Request) ParseURI() {
	if req.parsedURI {
		return
	}
	req.URI.Parse(req.Header.host, req.Header.RequestURI)
	req.parsedURI = true
}

// ParsePostArgs parses args sent in POST body and fills Request.PostArgs.
func (req *Request) ParsePostArgs() error {
	if req.parsedPostArgs {
		return nil
	}

	if !req.Header.IsMethodPost() {
		return fmt.Errorf("Cannot parse POST args for %q request", req.Header.Method)
	}
	if !bytes.Equal(req.Header.contentType, strPostArgsContentType) {
		return fmt.Errorf("Cannot parse POST args for %q Content-Type. Required %q Content-Type",
			req.Header.contentType, strPostArgsContentType)
	}
	req.PostArgs.ParseBytes(req.Body)
	req.parsedPostArgs = true
	return nil
}

// Clear clears request contents.
func (req *Request) Clear() {
	req.Header.Clear()
	req.clearSkipHeader()
}

func (req *Request) clearSkipHeader() {
	req.Body = req.Body[:0]
	req.URI.Clear()
	req.parsedURI = false
	req.PostArgs.Clear()
	req.parsedPostArgs = false
}

// Clear clears response contents.
func (resp *Response) Clear() {
	resp.Header.Clear()
	resp.clearSkipHeader()
}

func (resp *Response) clearSkipHeader() {
	resp.Body = resp.Body[:0]
	resp.BodyStream = nil
}

// Read reads request (including body) from the given r.
func (req *Request) Read(r *bufio.Reader) error {
	req.clearSkipHeader()
	err := req.Header.Read(r)
	if err != nil {
		return err
	}

	if req.Header.IsMethodPost() {
		req.Body, err = readBody(r, req.Header.ContentLength, req.Body)
		if err != nil {
			req.Clear()
			return err
		}
		req.Header.ContentLength = len(req.Body)
	}
	return nil
}

// Read reads response (including body) from the given r.
func (resp *Response) Read(r *bufio.Reader) error {
	resp.clearSkipHeader()
	err := resp.Header.Read(r)
	if err != nil {
		return err
	}

	if !isSkipResponseBody(resp.Header.StatusCode) && !resp.SkipBody {
		resp.Body, err = readBody(r, resp.Header.ContentLength, resp.Body)
		if err != nil {
			resp.Clear()
			return err
		}
		resp.Header.ContentLength = len(resp.Body)
	}
	return nil
}

func isSkipResponseBody(statusCode int) bool {
	// From http/1.1 specs:
	// All 1xx (informational), 204 (no content), and 304 (not modified) responses MUST NOT include a message-body
	if statusCode >= 100 && statusCode < 200 {
		return true
	}
	return statusCode == StatusNoContent || statusCode == StatusNotModified
}

// Write write request to w.
//
// Write doesn't flush request to w for performance reasons.
func (req *Request) Write(w *bufio.Writer) error {
	contentLengthOld := req.Header.ContentLength
	req.Header.ContentLength = len(req.Body)
	err := req.Header.Write(w)
	req.Header.ContentLength = contentLengthOld
	if err != nil {
		return err
	}
	if req.Header.IsMethodPost() {
		_, err = w.Write(req.Body)
	} else if len(req.Body) > 0 {
		return fmt.Errorf("Non-zero body for non-POST request. body=%q", req.Body)
	}
	return err
}

// Write writes response to w.
//
// Write doesn't flush response to w for performance reasons.
func (resp *Response) Write(w *bufio.Writer) error {
	var err error
	if resp.BodyStream != nil {
		if resp.Header.ContentLength > 0 {
			if err = resp.Header.Write(w); err != nil {
				return err
			}
			if err = writeBodyFixedSize(w, resp.BodyStream, resp.Header.ContentLength); err != nil {
				return err
			}
		} else {
			resp.Header.ContentLength = -1
			if err = resp.Header.Write(w); err != nil {
				return err
			}
			if err = writeBodyChunked(w, resp.BodyStream); err != nil {
				return err
			}
		}
		if bsc, ok := resp.BodyStream.(io.Closer); ok {
			err = bsc.Close()
		}
		return err
	}

	resp.Header.ContentLength = len(resp.Body)
	if err = resp.Header.Write(w); err != nil {
		return err
	}
	_, err = w.Write(resp.Body)
	return err
}

func writeBodyChunked(w *bufio.Writer, r io.Reader) error {
	vbuf := copyBufPool.Get()
	if vbuf == nil {
		vbuf = make([]byte, 4096)
	}
	buf := vbuf.([]byte)

	var err error
	var n int
	for {
		n, err = r.Read(buf)
		if n == 0 {
			if err == nil {
				panic("BUG: io.Reader returned 0, nil")
			}
			if err == io.EOF {
				if err = writeChunk(w, buf[:0]); err != nil {
					break
				}
				err = nil
			}
			break
		}
		if err = writeChunk(w, buf[:n]); err != nil {
			break
		}
	}

	copyBufPool.Put(vbuf)
	return err
}

var limitReaderPool sync.Pool

func writeBodyFixedSize(w *bufio.Writer, r io.Reader, size int) error {
	vbuf := copyBufPool.Get()
	if vbuf == nil {
		vbuf = make([]byte, 4096)
	}
	buf := vbuf.([]byte)

	vlr := limitReaderPool.Get()
	if vlr == nil {
		vlr = &io.LimitedReader{}
	}
	lr := vlr.(*io.LimitedReader)
	lr.R = r
	lr.N = int64(size)

	n, err := io.CopyBuffer(w, lr, buf)

	limitReaderPool.Put(vlr)
	copyBufPool.Put(vbuf)

	if n != int64(size) && err == nil {
		err = fmt.Errorf("read %d bytes from BodyStream instead of %d bytes", n, size)
	}
	return err
}

func writeChunk(w *bufio.Writer, b []byte) error {
	n := len(b)
	writeHexInt(w, n)
	w.Write(strCRLF)
	w.Write(b)
	_, err := w.Write(strCRLF)
	return err
}

var copyBufPool sync.Pool

func readBody(r *bufio.Reader, contentLength int, dst []byte) ([]byte, error) {
	dst = dst[:0]
	if contentLength >= 0 {
		return appendBodyFixedSize(r, dst, contentLength)
	}
	if contentLength == -1 {
		return readBodyChunked(r, dst)
	}
	return readBodyIdentity(r, dst)
}

func readBodyIdentity(r *bufio.Reader, dst []byte) ([]byte, error) {
	dst = dst[:cap(dst)]
	if len(dst) == 0 {
		dst = make([]byte, 1024)
	}
	offset := 0
	for {
		nn, err := r.Read(dst[offset:])
		if nn <= 0 {
			if err != nil {
				if err == io.EOF {
					return dst[:offset], nil
				}
				return dst[:offset], err
			}
			panic(fmt.Sprintf("BUG: bufio.Read() returned (%d, nil)", nn))
		}
		offset += nn
		if len(dst) == offset {
			b := make([]byte, round2(2*offset))
			copy(b, dst)
			dst = b
		}
	}
}

func appendBodyFixedSize(r *bufio.Reader, dst []byte, n int) ([]byte, error) {
	if n == 0 {
		return dst, nil
	}

	offset := len(dst)
	dstLen := offset + n
	if cap(dst) < dstLen {
		b := make([]byte, round2(dstLen))
		copy(b, dst)
		dst = b
	}
	dst = dst[:dstLen]

	for {
		nn, err := r.Read(dst[offset:])
		if nn <= 0 {
			if err != nil {
				if err == io.EOF {
					err = io.ErrUnexpectedEOF
				}
				return dst[:offset], err
			}
			panic(fmt.Sprintf("BUG: bufio.Read() returned (%d, nil)", nn))
		}
		offset += nn
		if offset == dstLen {
			return dst, nil
		}
	}
}

func readBodyChunked(r *bufio.Reader, dst []byte) ([]byte, error) {
	if len(dst) > 0 {
		panic("BUG: expected zero-length buffer")
	}

	strCRLFLen := len(strCRLF)
	for {
		chunkSize, err := parseChunkSize(r)
		if err != nil {
			return dst, err
		}
		dst, err = appendBodyFixedSize(r, dst, chunkSize+strCRLFLen)
		if err != nil {
			return dst, err
		}
		if !bytes.Equal(dst[len(dst)-strCRLFLen:], strCRLF) {
			return dst, fmt.Errorf("cannot find crlf at the end of chunk")
		}
		dst = dst[:len(dst)-strCRLFLen]
		if chunkSize == 0 {
			return dst, nil
		}
	}
}

func parseChunkSize(r *bufio.Reader) (int, error) {
	n, err := readHexInt(r)
	if err != nil {
		return -1, err
	}
	c, err := r.ReadByte()
	if err != nil {
		return -1, fmt.Errorf("cannot read '\r' char at the end of chunk size: %s", err)
	}
	if c != '\r' {
		return -1, fmt.Errorf("unexpected char %q at the end of chunk size. Expected %q", c, '\r')
	}
	c, err = r.ReadByte()
	if err != nil {
		return -1, fmt.Errorf("cannot read '\n' char at the end of chunk size: %s", err)
	}
	if c != '\n' {
		return -1, fmt.Errorf("unexpected char %q at the end of chunk size. Expected %q", c, '\n')
	}
	return n, nil
}

func round2(n int) int {
	if n <= 0 {
		return 0
	}
	n--
	x := uint(0)
	for n > 0 {
		n >>= 1
		x++
	}
	return 1 << x
}
<commit_msg>Mention that client never sets BodyStream on response<commit_after>package fasthttp

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"sync"
)

// Request represents HTTP request.
//
// It is forbidden copying Request instances. Create new instances
// and use CopyTo() instead.
type Request struct {
	// Request header
	Header RequestHeader

	// Request body.
	Body []byte

	// Request URI.
	// URI becomes available only after Request.ParseURI() call.
	URI       URI
	parsedURI bool

	// Arguments sent in POST.
	// PostArgs becomes available only after Request.ParsePostArgs() call.
	PostArgs       Args
	parsedPostArgs bool
}

// Response represents HTTP response.
//
// It is forbidden copying Response instances. Create new instances
// and use CopyTo() instead.
type Response struct {
	// Response header
	Header ResponseHeader

	// Response body.
	//
	// Either Body or BodyStream may be set, but not both.
	Body []byte

	// Response body stream.
	//
	// BodyStream may be set instead of Body for performance reasons only.
	//
	// BodyStream is read by Response.Write() until one of the following
	// events occur:
	// - Response.ContentLength bytes read if it is greater than 0.
	// - error or io.EOF is returned from BodyStream if Response.ContentLength
	// is 0 or negative.
	//
	// Response.Write() calls BodyStream.Close() (io.Closer) if such method
	// is present after finishing reading BodyStream.
	//
	// Either BodyStream or Body may be set, but not both.
	//
	// Client and Response.Read() never sets BodyStream - it sets only Body.
	BodyStream io.Reader

	// If set to true, Response.Read() skips reading body.
	// Use it for HEAD requests.
	SkipBody bool
}

// CopyTo copies req contents to dst.
func (req *Request) CopyTo(dst *Request) {
	dst.Clear()
	req.Header.CopyTo(&dst.Header)
	dst.Body = append(dst.Body[:0], req.Body...)
	if req.parsedURI {
		dst.ParseURI()
	}
	if req.parsedPostArgs {
		dst.ParsePostArgs()
	}
}

// CopyTo copies resp contents to dst except of BodyStream.
func (resp *Response) CopyTo(dst *Response) {
	dst.Clear()
	resp.Header.CopyTo(&dst.Header)
	dst.Body = append(dst.Body[:0], resp.Body...)
	dst.SkipBody = resp.SkipBody
}

// ParseURI parses request uri and fills Request.URI.
func (req *Request) ParseURI() {
	if req.parsedURI {
		return
	}
	req.URI.Parse(req.Header.host, req.Header.RequestURI)
	req.parsedURI = true
}

// ParsePostArgs parses args sent in POST body and fills Request.PostArgs.
func (req *Request) ParsePostArgs() error {
	if req.parsedPostArgs {
		return nil
	}

	if !req.Header.IsMethodPost() {
		return fmt.Errorf("Cannot parse POST args for %q request", req.Header.Method)
	}
	if !bytes.Equal(req.Header.contentType, strPostArgsContentType) {
		return fmt.Errorf("Cannot parse POST args for %q Content-Type. Required %q Content-Type",
			req.Header.contentType, strPostArgsContentType)
	}
	req.PostArgs.ParseBytes(req.Body)
	req.parsedPostArgs = true
	return nil
}

// Clear clears request contents.
func (req *Request) Clear() {
	req.Header.Clear()
	req.clearSkipHeader()
}

func (req *Request) clearSkipHeader() {
	req.Body = req.Body[:0]
	req.URI.Clear()
	req.parsedURI = false
	req.PostArgs.Clear()
	req.parsedPostArgs = false
}

// Clear clears response contents.
func (resp *Response) Clear() {
	resp.Header.Clear()
	resp.clearSkipHeader()
}

func (resp *Response) clearSkipHeader() {
	resp.Body = resp.Body[:0]
	resp.BodyStream = nil
}

// Read reads request (including body) from the given r.
func (req *Request) Read(r *bufio.Reader) error {
	req.clearSkipHeader()
	err := req.Header.Read(r)
	if err != nil {
		return err
	}

	if req.Header.IsMethodPost() {
		req.Body, err = readBody(r, req.Header.ContentLength, req.Body)
		if err != nil {
			req.Clear()
			return err
		}
		req.Header.ContentLength = len(req.Body)
	}
	return nil
}

// Read reads response (including body) from the given r.
func (resp *Response) Read(r *bufio.Reader) error {
	resp.clearSkipHeader()
	err := resp.Header.Read(r)
	if err != nil {
		return err
	}

	if !isSkipResponseBody(resp.Header.StatusCode) && !resp.SkipBody {
		resp.Body, err = readBody(r, resp.Header.ContentLength, resp.Body)
		if err != nil {
			resp.Clear()
			return err
		}
		resp.Header.ContentLength = len(resp.Body)
	}
	return nil
}

func isSkipResponseBody(statusCode int) bool {
	// From http/1.1 specs:
	// All 1xx (informational), 204 (no content), and 304 (not modified) responses MUST NOT include a message-body
	if statusCode >= 100 && statusCode < 200 {
		return true
	}
	return statusCode == StatusNoContent || statusCode == StatusNotModified
}

// Write write request to w.
//
// Write doesn't flush request to w for performance reasons.
func (req *Request) Write(w *bufio.Writer) error {
	contentLengthOld := req.Header.ContentLength
	req.Header.ContentLength = len(req.Body)
	err := req.Header.Write(w)
	req.Header.ContentLength = contentLengthOld
	if err != nil {
		return err
	}
	if req.Header.IsMethodPost() {
		_, err = w.Write(req.Body)
	} else if len(req.Body) > 0 {
		return fmt.Errorf("Non-zero body for non-POST request. body=%q", req.Body)
	}
	return err
}

// Write writes response to w.
//
// Write doesn't flush response to w for performance reasons.
func (resp *Response) Write(w *bufio.Writer) error {
	var err error
	if resp.BodyStream != nil {
		if resp.Header.ContentLength > 0 {
			if err = resp.Header.Write(w); err != nil {
				return err
			}
			if err = writeBodyFixedSize(w, resp.BodyStream, resp.Header.ContentLength); err != nil {
				return err
			}
		} else {
			resp.Header.ContentLength = -1
			if err = resp.Header.Write(w); err != nil {
				return err
			}
			if err = writeBodyChunked(w, resp.BodyStream); err != nil {
				return err
			}
		}
		if bsc, ok := resp.BodyStream.(io.Closer); ok {
			err = bsc.Close()
		}
		return err
	}

	resp.Header.ContentLength = len(resp.Body)
	if err = resp.Header.Write(w); err != nil {
		return err
	}
	_, err = w.Write(resp.Body)
	return err
}

func writeBodyChunked(w *bufio.Writer, r io.Reader) error {
	vbuf := copyBufPool.Get()
	if vbuf == nil {
		vbuf = make([]byte, 4096)
	}
	buf := vbuf.([]byte)

	var err error
	var n int
	for {
		n, err = r.Read(buf)
		if n == 0 {
			if err == nil {
				panic("BUG: io.Reader returned 0, nil")
			}
			if err == io.EOF {
				if err = writeChunk(w, buf[:0]); err != nil {
					break
				}
				err = nil
			}
			break
		}
		if err = writeChunk(w, buf[:n]); err != nil {
			break
		}
	}

	copyBufPool.Put(vbuf)
	return err
}

var limitReaderPool sync.Pool

func writeBodyFixedSize(w *bufio.Writer, r io.Reader, size int) error {
	vbuf := copyBufPool.Get()
	if vbuf == nil {
		vbuf = make([]byte, 4096)
	}
	buf := vbuf.([]byte)

	vlr := limitReaderPool.Get()
	if vlr == nil {
		vlr = &io.LimitedReader{}
	}
	lr := vlr.(*io.LimitedReader)
	lr.R = r
	lr.N = int64(size)

	n, err := io.CopyBuffer(w, lr, buf)

	limitReaderPool.Put(vlr)
	copyBufPool.Put(vbuf)

	if n != int64(size) && err == nil {
		err = fmt.Errorf("read %d bytes from BodyStream instead of %d bytes", n, size)
	}
	return err
}

func writeChunk(w *bufio.Writer, b []byte) error {
	n := len(b)
	writeHexInt(w, n)
	w.Write(strCRLF)
	w.Write(b)
	_, err := w.Write(strCRLF)
	return err
}

var copyBufPool sync.Pool

func readBody(r *bufio.Reader, contentLength int, dst []byte) ([]byte, error) {
	dst = dst[:0]
	if contentLength >= 0 {
		return appendBodyFixedSize(r, dst, contentLength)
	}
	if contentLength == -1 {
		return readBodyChunked(r, dst)
	}
	return readBodyIdentity(r, dst)
}

func readBodyIdentity(r *bufio.Reader, dst []byte) ([]byte, error) {
	dst = dst[:cap(dst)]
	if len(dst) == 0 {
		dst = make([]byte, 1024)
	}
	offset := 0
	for {
		nn, err := r.Read(dst[offset:])
		if nn <= 0 {
			if err != nil {
				if err == io.EOF {
					return dst[:offset], nil
				}
				return dst[:offset], err
			}
			panic(fmt.Sprintf("BUG: bufio.Read() returned (%d, nil)", nn))
		}
		offset += nn
		if len(dst) == offset {
			b := make([]byte, round2(2*offset))
			copy(b, dst)
			dst = b
		}
	}
}

func appendBodyFixedSize(r *bufio.Reader, dst []byte, n int) ([]byte, error) {
	if n == 0 {
		return dst, nil
	}

	offset := len(dst)
	dstLen := offset + n
	if cap(dst) < dstLen {
		b := make([]byte, round2(dstLen))
		copy(b, dst)
		dst = b
	}
	dst = dst[:dstLen]

	for {
		nn, err := r.Read(dst[offset:])
		if nn <= 0 {
			if err != nil {
				if err == io.EOF {
					err = io.ErrUnexpectedEOF
				}
				return dst[:offset], err
			}
			panic(fmt.Sprintf("BUG: bufio.Read() returned (%d, nil)", nn))
		}
		offset += nn
		if offset == dstLen {
			return dst, nil
		}
	}
}

func readBodyChunked(r *bufio.Reader, dst []byte) ([]byte, error) {
	if len(dst) > 0 {
		panic("BUG: expected zero-length buffer")
	}

	strCRLFLen := len(strCRLF)
	for {
		chunkSize, err := parseChunkSize(r)
		if err != nil {
			return dst, err
		}
		dst, err = appendBodyFixedSize(r, dst, chunkSize+strCRLFLen)
		if err != nil {
			return dst, err
		}
		if !bytes.Equal(dst[len(dst)-strCRLFLen:], strCRLF) {
			return dst, fmt.Errorf("cannot find crlf at the end of chunk")
		}
		dst = dst[:len(dst)-strCRLFLen]
		if chunkSize == 0 {
			return dst, nil
		}
	}
}

func parseChunkSize(r *bufio.Reader) (int, error) {
	n, err := readHexInt(r)
	if err != nil {
		return -1, err
	}
	c, err := r.ReadByte()
	if err != nil {
		return -1, fmt.Errorf("cannot read '\r' char at the end of chunk size: %s", err)
	}
	if c != '\r' {
		return -1, fmt.Errorf("unexpected char %q at the end of chunk size. Expected %q", c, '\r')
	}
	c, err = r.ReadByte()
	if err != nil {
		return -1, fmt.Errorf("cannot read '\n' char at the end of chunk size: %s", err)
	}
	if c != '\n' {
		return -1, fmt.Errorf("unexpected char %q at the end of chunk size. Expected %q", c, '\n')
	}
	return n, nil
}

func round2(n int) int {
	if n <= 0 {
		return 0
	}
	n--
	x := uint(0)
	for n > 0 {
		n >>= 1
		x++
	}
	return 1 << x
}
<|endoftext|>


<commit_before>package bagman

import (
	"archive/tar"
	"path/filepath"
	"errors"
	"fmt"
	"io"
	"os"
	"sort"
	"strings"
	"github.com/APTrust/bagins"
)

type TarResult struct {
	InputFile       string
	OutputDir       string
	Error           error
	Warnings        []string
	FilesUnpacked   []string
}

const FILE      = '0'
const DIRECTORY = '5'

// Untars the file at the specified path and returns a list
// of files that were untarred from the archive. Check
// result.Error to ensure there were no errors.
func Untar(path string) (result *TarResult) {

	// Set up our result
	tarResult := new(TarResult)
	absInputFile, err := filepath.Abs(path)
	if err != nil {
		tarResult.Error = err
		return tarResult
	}
	tarResult.InputFile = absInputFile

	// Open the tar file for reading.
	file, err := os.Open(path)
	if err != nil {
		tarResult.Error = err
		return tarResult
	}
	defer file.Close()

	// Untar the file and record the results.
	tarReader := tar.NewReader(file)
	for {
		header, err := tarReader.Next();
		if err != nil && err.Error() == "EOF" {
			break  // end of archive
		}
		if err != nil {
			tarResult.Error = err
			return tarResult
		}
		outputPath := filepath.Join(filepath.Dir(absInputFile), header.Name)
		tarDirectory := strings.Split(header.Name, "/")[0]
		if tarResult.OutputDir == "" {
			tarResult.OutputDir = filepath.Join(filepath.Dir(absInputFile), tarDirectory)
		}

		// Make sure the directory that we're about to write into exists.
		err = os.MkdirAll(filepath.Dir(outputPath), 0755)
		if err != nil {
			tarResult.Error = err
			return tarResult
		}

		// Copy the file, if it's an actual file. Otherwise, ignore it and record
		// a warning. The bag library does not deal with items like symlinks.
		if header.Typeflag == FILE {
			outputWriter, err := os.OpenFile(outputPath, os.O_CREATE | os.O_WRONLY, 0644)
			if err != nil {
				tarResult.Error = err
				return tarResult
			}
			defer outputWriter.Close()
			io.Copy(outputWriter, tarReader);
			outputRelativePath := strings.Replace(outputPath, tarResult.OutputDir + "/", "", 1)
			tarResult.FilesUnpacked = append(tarResult.FilesUnpacked, outputRelativePath)
		} else if header.Typeflag != DIRECTORY {
			tarResult.Warnings = append(tarResult.Warnings,
				fmt.Sprintf("Ignoring item %s of type %c because it's neither a file nor a directory",
					header.Name, header.Typeflag))
		}
	}
	sort.Strings(tarResult.FilesUnpacked)
	return tarResult
}

type BagReadResult struct {
	Path             string
	Files            []string
	Error            error
	Tags             []bagins.TagField
	ChecksumErrors   []error
}

// Reads an untarred bag. The path parameter should be a path to
// a directory that contains the bag, info and manifest files.
// The bag content should be in the data directory under path.
// Check result.Error to ensure there were no errors.
func ReadBag(path string) (result *BagReadResult) {
	bagReadResult := new(BagReadResult)
	bagReadResult.Path = path

	bag, err := bagins.ReadBag(path, []string{"bagit.txt", "bag-info.txt", "aptrust-info.txt"}, "") //"manifest-md5.txt")
	if err != nil {
		bagReadResult.Error = err
		return bagReadResult
	}

	fileNames, err := bag.ListFiles()
	if err!= nil {
		bagReadResult.Error = err
		return bagReadResult
	}
	bagReadResult.Files = make([]string, len(fileNames))
	copy(bagReadResult.Files, fileNames)

	bagInfo, err := bag.BagInfo()
	if err != nil {
		bagReadResult.Error = err
		return bagReadResult
	}
	tagFields := bagInfo.Data.Fields()
	bagReadResult.Tags = make([]bagins.TagField, len(tagFields))
	copy(bagReadResult.Tags, tagFields)

	checksumErrors := bag.Manifest.RunChecksums()
	if len(checksumErrors) > 0 {
		bagReadResult.Error = errors.New("One or more checksums were invalid.")
		bagReadResult.ChecksumErrors = make([]error, len(checksumErrors))
		copy(bagReadResult.ChecksumErrors, checksumErrors)
	}
	return bagReadResult
}
<commit_msg>Use constants from tar package<commit_after>package bagman

import (
	"archive/tar"
	"path/filepath"
	"errors"
	"fmt"
	"io"
	"os"
	"sort"
	"strings"
	"github.com/APTrust/bagins"
)

type TarResult struct {
	InputFile       string
	OutputDir       string
	Error           error
	Warnings        []string
	FilesUnpacked   []string
}

// Untars the file at the specified path and returns a list
// of files that were untarred from the archive. Check
// result.Error to ensure there were no errors.
func Untar(path string) (result *TarResult) {

	// Set up our result
	tarResult := new(TarResult)
	absInputFile, err := filepath.Abs(path)
	if err != nil {
		tarResult.Error = err
		return tarResult
	}
	tarResult.InputFile = absInputFile

	// Open the tar file for reading.
	file, err := os.Open(path)
	if err != nil {
		tarResult.Error = err
		return tarResult
	}
	defer file.Close()

	// Untar the file and record the results.
	tarReader := tar.NewReader(file)
	for {
		header, err := tarReader.Next();
		if err != nil && err.Error() == "EOF" {
			break  // end of archive
		}
		if err != nil {
			tarResult.Error = err
			return tarResult
		}
		outputPath := filepath.Join(filepath.Dir(absInputFile), header.Name)
		tarDirectory := strings.Split(header.Name, "/")[0]
		if tarResult.OutputDir == "" {
			tarResult.OutputDir = filepath.Join(filepath.Dir(absInputFile), tarDirectory)
		}

		// Make sure the directory that we're about to write into exists.
		err = os.MkdirAll(filepath.Dir(outputPath), 0755)
		if err != nil {
			tarResult.Error = err
			return tarResult
		}

		// Copy the file, if it's an actual file. Otherwise, ignore it and record
		// a warning. The bag library does not deal with items like symlinks.
		if header.Typeflag == tar.TypeReg || header.Typeflag == tar.TypeRegA {
			outputWriter, err := os.OpenFile(outputPath, os.O_CREATE | os.O_WRONLY, 0644)
			if err != nil {
				tarResult.Error = err
				return tarResult
			}
			defer outputWriter.Close()
			io.Copy(outputWriter, tarReader);
			outputRelativePath := strings.Replace(outputPath, tarResult.OutputDir + "/", "", 1)
			tarResult.FilesUnpacked = append(tarResult.FilesUnpacked, outputRelativePath)
		} else if header.Typeflag != tar.TypeDir {
			tarResult.Warnings = append(tarResult.Warnings,
				fmt.Sprintf("Ignoring item %s of type %c because it's neither a file nor a directory",
					header.Name, header.Typeflag))
		}
	}
	sort.Strings(tarResult.FilesUnpacked)
	return tarResult
}

type BagReadResult struct {
	Path             string
	Files            []string
	Error            error
	Tags             []bagins.TagField
	ChecksumErrors   []error
}

// Reads an untarred bag. The path parameter should be a path to
// a directory that contains the bag, info and manifest files.
// The bag content should be in the data directory under path.
// Check result.Error to ensure there were no errors.
func ReadBag(path string) (result *BagReadResult) {
	bagReadResult := new(BagReadResult)
	bagReadResult.Path = path

	bag, err := bagins.ReadBag(path, []string{"bagit.txt", "bag-info.txt", "aptrust-info.txt"}, "") //"manifest-md5.txt")
	if err != nil {
		bagReadResult.Error = err
		return bagReadResult
	}

	fileNames, err := bag.ListFiles()
	if err!= nil {
		bagReadResult.Error = err
		return bagReadResult
	}
	bagReadResult.Files = make([]string, len(fileNames))
	copy(bagReadResult.Files, fileNames)

	bagInfo, err := bag.BagInfo()
	if err != nil {
		bagReadResult.Error = err
		return bagReadResult
	}
	tagFields := bagInfo.Data.Fields()
	bagReadResult.Tags = make([]bagins.TagField, len(tagFields))
	copy(bagReadResult.Tags, tagFields)

	checksumErrors := bag.Manifest.RunChecksums()
	if len(checksumErrors) > 0 {
		bagReadResult.Error = errors.New("One or more checksums were invalid.")
		bagReadResult.ChecksumErrors = make([]error, len(checksumErrors))
		copy(bagReadResult.ChecksumErrors, checksumErrors)
	}
	return bagReadResult
}
<|endoftext|>


<commit_before>/*
	Package hwio implements a simple Arduino-like interface for controlling
	hardware I/O, with configurable backends depending on the device.
*/
package hwio

import (
	"errors"
	"fmt"
	"time"
	"os/exec"
	"strings"
)

type BitShiftOrder byte

const (
	LSBFIRST BitShiftOrder = iota
	MSBFIRST
)

// Reference to driver we're using
var driver HardwareDriver

// Retrieved from the driver, this is the map of the hardware pins supported by
// the driver and their capabilities
var definedPins HardwarePinMap

// A private type for associating a pin's definition with the current IO mode
// and any other dynamic properties of the pin.
type assignedPin struct {
	pinDef    *PinDef   // definition of pin
	pinIOMode PinIOMode // mode that was assigned to this pin
}

// A map of pin numbers to the assigned dynamic properties of the pin. This is
// set by PinMode when errorChecking is on, and can be used by other functions
// to determine if the request is valid given the assigned properties of the pin.
var assignedPins map[Pin]*assignedPin

// If set to true, functions should test that their constraints are met.
// e.g. test that the pin is capable of doing what is asked. This can be set
// with SetErrorChecking(). Setting to false bypasses checks for performance.
// By default turned on, which is a better default for beginners.
var errorChecking bool = true

// init() attempts to determine from the environment what the driver is. The
// intent is that the consumer of the library would not generally have to worry
// about it, it would just work. If it cannot determine the driver, it doesn't
// set the driver to anything.
func init() {
	determineDriver()
}

// Work out the driver from environment if we can. If we have any problems,
// don't generate an error, just return with the driver not set.
// @todo use reflection to determine all implementors of the driver interface, and
// @todo   call a method on the interface to self-detect. init and
// @todo   constructor of drivers should do no setup in this case, esp of hardware
func determineDriver() {
	uname, e := exec.Command("uname", "-a").Output()
	if e != nil {
		return
	}

	s := string(uname)
	if strings.Contains(s, "beaglebone") {
		SetDriver(new(BeagleBoneDriver))
	} else if strings.Contains(s, "raspberrypi") && strings.Contains(s, "adafruit") {
		SetDriver(new(RaspberryPiDriver))
	}
}

// Check if the driver is assigned. If not, return an error to indicate that,
// otherwise return no error.
func assertDriver() error {
	if driver == nil {
		return errors.New("hwio has no configured driver")
	}
	return nil
}

// Set the driver. Also calls Init on the driver, and loads the capabilities
// of the device.
func SetDriver(d HardwareDriver) {
	driver = d
	driver.Init()
	definedPins = driver.PinMap()
	assignedPins = make(map[Pin]*assignedPin)
}

// Retrieve the current hardware driver.
func GetDriver() HardwareDriver {
	return driver
}

// Returns a map of the hardware pins. This will only work once the driver is
// set.
func GetDefinedPins() HardwarePinMap {
	return definedPins
}

// Returns a Pin given a canonical name for the pin.
// e.g. to get the pin number of P8.13 on a beaglebone,
//     pin := hwio.GetPin("P8.13")
// Order of search is:
// - search hwRefs in the pin map in order.
// This function should not generally be relied on for performance. For max speed, call this
// for each pin you use once on init, and use the returned Pin values thereafter.
// Search is case sensitive at the moment
// @todo GetPin: consider making it case-insensitive on name
// @todo GetPin: consider allowing an int or int as string to identify logical pin directly
func GetPin(cname string) (Pin, error) {
	for pin, pinDef := range definedPins {
		for _,name := range pinDef.hwPinRefs {
			if name == cname {
				return pin, nil
			}
		}
	}
	return Pin(0), errors.New(fmt.Sprintf("Could not find a pin called %s", cname))
}

// Set error checking. This should be called before pin assignments.
func SetErrorChecking(check bool) {
	errorChecking = check
}

// Set the mode of a pin. Analogous to Arduino pin mode.
func PinMode(pin Pin, mode PinIOMode) (e error) {
	if errorChecking {
		if e = assertDriver(); e != nil {
			return
		}

		pd := definedPins[pin]
		if pd == nil {
			return errors.New(fmt.Sprintf("Pin %d is not defined by the current driver", pin))
		}

		if e = checkPinMode(mode, pd); e != nil {
			return
		}

		// assign this pin
		assignedPins[pin] = &assignedPin{pinDef: pd, pinIOMode: mode}
	}

	return driver.PinMode(pin, mode)
}

func checkPinMode(mode PinIOMode, pd *PinDef) (e error) {
	ok := false
	switch mode {
	case INPUT:
		ok = pd.HasCapability(CAP_INPUT) || pd.HasCapability(CAP_ANALOG_IN)
	case OUTPUT:
		ok = pd.HasCapability(CAP_OUTPUT)
	case INPUT_PULLUP:
		ok = pd.HasCapability(CAP_INPUT_PULLUP)
	case INPUT_PULLDOWN:
		ok = pd.HasCapability(CAP_INPUT_PULLDOWN)
	}
	if ok {
		return nil
	}
	return errors.New(fmt.Sprintf("Pin %d can't be set to mode %s because it does not support that capability", pd.pin, mode.String()))
}

// Write a value to a digital pin
func DigitalWrite(pin Pin, value int) (e error) {
	if errorChecking {
		if e = assertDriver(); e != nil {
			return
		}

		a := assignedPins[pin]
		if a == nil {
			return errors.New(fmt.Sprintf("DigitalWrite: pin %d mode has not been set", pin))
		}
		if a.pinIOMode != OUTPUT {
			return errors.New(fmt.Sprintf("DigitalWrite: pin %d mode is not set for output", pin))
		}
	}

	return driver.DigitalWrite(pin, value)
}

// Read a value from a digital pin
func DigitalRead(pin Pin) (result int, e error) {
	if errorChecking {
		if e = assertDriver(); e != nil {
			return 0, e
		}

		a := assignedPins[pin]
		if a == nil {
			e = errors.New(fmt.Sprintf("DigitalRead: pin %d mode has not been set", pin))
			return
		}
		if a.pinIOMode != INPUT && a.pinIOMode != INPUT_PULLUP && a.pinIOMode != INPUT_PULLDOWN {
			e = errors.New(fmt.Sprintf("DigitalRead: pin %d mode is not set for input", pin))
			return
		}
	}

	return driver.DigitalRead(pin)
}

// Read an analog value from a pin. The range of values is hardware driver dependent.
func AnalogRead(pin Pin) (result int, e error) {
	if errorChecking {
		if e = assertDriver(); e != nil {
			return
		}

		a := assignedPins[pin]
		if a == nil {
			e = errors.New(fmt.Sprintf("AnalogRead: pin %d mode has not been set", pin))
			return
		}
		if a.pinIOMode != INPUT && a.pinIOMode != INPUT_PULLUP {
			e = errors.New(fmt.Sprintf("AnalogRead: pin %d mode is not set for input", pin))
			return
		}
	}

	return driver.AnalogRead(pin)
}

// Write an analog value. The interpretation is hardware dependent, but is
// generally implemented using PWM.
func AnalogWrite(pin Pin, value int) (e error) {
	if errorChecking {
		if e = assertDriver(); e != nil {
			return
		}

		a := assignedPins[pin]
		if a == nil {
			return errors.New(fmt.Sprintf("AnalogWrite: pin %d mode has not been set", pin))
		}
		if a.pinIOMode != OUTPUT {
			return errors.New(fmt.Sprintf("AnalogWrite: pin %d mode is not set for output", pin))
		}
	}

	return driver.AnalogWrite(pin, value)
}

// Delay execution by the specified number of milliseconds. This is a helper
// function for similarity with Arduino. It is implemented using standard go
// time package.
func Delay(duration int) {
	time.Sleep(time.Duration(duration) * time.Millisecond)
}

// Delay execution by the specified number of microseconds. This is a helper
// function for similarity with Arduino. It is implemented using standard go
// time package
func DelayMicroseconds(duration int) {
	time.Sleep(time.Duration(duration) * time.Microsecond)
}

// @todo DebugPinMap: sort
func DebugPinMap() {
	fmt.Println("HardwarePinMap:")
	for key, val := range definedPins {
		fmt.Printf("Pin %d: %s\n", key, val.String())
	}
	fmt.Printf("\n")
}

// The approximate mapping of Arduino shiftOut, this shifts a byte out on the
// data pin, pulsing the clock pin high and then low.
func ShiftOut(dataPin Pin, clockPin Pin, value uint, order BitShiftOrder) error {
	return ShiftOutSize(dataPin, clockPin, value, order, 8)
}

// More generic version of ShiftOut which shifts out n of data from value. The
// value shifted out is always the lowest n bits of the value, but 'order'
// determines whether the msb or lsb from that value are shifted first
func ShiftOutSize(dataPin Pin, clockPin Pin, value uint, order BitShiftOrder, n uint) error {
	bit := uint(0)
	v := value
	mask := uint(1) << (n - 1)
	for i := uint(0); i < n; i++ {
		// get the next bit
		if order == LSBFIRST {
			bit = v & 1
			v = v >> 1
		} else {
			bit = v & mask
			if bit != 0 {
				bit = 1
			}
			v = v << 1
		}
		// write to data pin
		e := DigitalWrite(dataPin, int(bit))
		if e != nil {
			return e
		}
		// pulse clock high and then low
		e = DigitalWrite(clockPin, HIGH)
		if e != nil {
			return e
		}
		DigitalWrite(clockPin, LOW)
	}
	return nil
}

// def toggle(gpio_pin):
//   """ Toggles the state of the given digital pin. """
//   assert (gpio_pin in GPIO), "*Invalid GPIO pin: '%s'" % gpio_pin
//   _xorReg(GPIO[gpio_pin][0]+GPIO_DATAOUT, GPIO[gpio_pin][1])

// def pinState(gpio_pin):
//   """ Returns the state of a digital pin if it is configured as
//       an output. Returns None if it is configuredas an input. """
//   assert (gpio_pin in GPIO), "*Invalid GPIO pin: '%s'" % gpio_pin
//   if (_getReg(GPIO[gpio_pin][0]+GPIO_OE) & GPIO[gpio_pin][1]):
//     return None
//   if (_getReg(GPIO[gpio_pin][0]+GPIO_DATAOUT) & GPIO[gpio_pin][1]):
//     return HIGH
//   return LOW

// @todo Implement other core Arduino function equivalents:
//	AnalogReference
//	Tone
//	NoTone
//	ShiftOut
//	ShiftIn
//	PulseIn
//	Millis
//	Micros
//	RandomSeed
//	Random
//	AttachInterupt
//	DetachInterupt

// This is the interface that hardware drivers implement.
type HardwareDriver interface {
	// Initialise the driver after creation
	Init() (e error)

	// Set mode of a pin
	PinMode(pin Pin, mode PinIOMode) (e error)

	// Write digital output
	DigitalWrite(pin Pin, value int) error

	// Read digital input
	DigitalRead(Pin) (int, error)

	// PWM write
	AnalogWrite(pin Pin, value int) error

	// Analog input. Resolution is device dependent.
	AnalogRead(pin Pin) (int, error)

	// Return the pin map for the driver, listing all supported pins and their capabilities
	PinMap() (pinMap HardwarePinMap)

	// Close the driver before destruction
	Close()
}
<commit_msg>barf on driver init error<commit_after>/*
	Package hwio implements a simple Arduino-like interface for controlling
	hardware I/O, with configurable backends depending on the device.
*/
package hwio

import (
	"errors"
	"fmt"
	"time"
	"os/exec"
	"strings"
)

type BitShiftOrder byte

const (
	LSBFIRST BitShiftOrder = iota
	MSBFIRST
)

// Reference to driver we're using
var driver HardwareDriver

// Retrieved from the driver, this is the map of the hardware pins supported by
// the driver and their capabilities
var definedPins HardwarePinMap

// A private type for associating a pin's definition with the current IO mode
// and any other dynamic properties of the pin.
type assignedPin struct {
	pinDef    *PinDef   // definition of pin
	pinIOMode PinIOMode // mode that was assigned to this pin
}

// A map of pin numbers to the assigned dynamic properties of the pin. This is
// set by PinMode when errorChecking is on, and can be used by other functions
// to determine if the request is valid given the assigned properties of the pin.
var assignedPins map[Pin]*assignedPin

// If set to true, functions should test that their constraints are met.
// e.g. test that the pin is capable of doing what is asked. This can be set
// with SetErrorChecking(). Setting to false bypasses checks for performance.
// By default turned on, which is a better default for beginners.
var errorChecking bool = true

// init() attempts to determine from the environment what the driver is. The
// intent is that the consumer of the library would not generally have to worry
// about it, it would just work. If it cannot determine the driver, it doesn't
// set the driver to anything.
func init() {
	determineDriver()
}

// Work out the driver from environment if we can. If we have any problems,
// don't generate an error, just return with the driver not set.
// @todo use reflection to determine all implementors of the driver interface, and
// @todo   call a method on the interface to self-detect. init and
// @todo   constructor of drivers should do no setup in this case, esp of hardware
func determineDriver() {
	uname, e := exec.Command("uname", "-a").Output()
	if e != nil {
		return
	}

	s := string(uname)
	if strings.Contains(s, "beaglebone") {
		SetDriver(new(BeagleBoneDriver))
	} else if strings.Contains(s, "raspberrypi") && strings.Contains(s, "adafruit") {
		SetDriver(new(RaspberryPiDriver))
	}
}

// Check if the driver is assigned. If not, return an error to indicate that,
// otherwise return no error.
func assertDriver() error {
	if driver == nil {
		return errors.New("hwio has no configured driver")
	}
	return nil
}

// Set the driver. Also calls Init on the driver, and loads the capabilities
// of the device.
func SetDriver(d HardwareDriver) {
	driver = d
	e := driver.Init()
	if e != nil {
		fmt.Printf("Could not initialise driver: %s", e)
	}
	definedPins = driver.PinMap()
	assignedPins = make(map[Pin]*assignedPin)
}

// Retrieve the current hardware driver.
func GetDriver() HardwareDriver {
	return driver
}

// Returns a map of the hardware pins. This will only work once the driver is
// set.
func GetDefinedPins() HardwarePinMap {
	return definedPins
}

// Returns a Pin given a canonical name for the pin.
// e.g. to get the pin number of P8.13 on a beaglebone,
//     pin := hwio.GetPin("P8.13")
// Order of search is:
// - search hwRefs in the pin map in order.
// This function should not generally be relied on for performance. For max speed, call this
// for each pin you use once on init, and use the returned Pin values thereafter.
// Search is case sensitive at the moment
// @todo GetPin: consider making it case-insensitive on name
// @todo GetPin: consider allowing an int or int as string to identify logical pin directly
func GetPin(cname string) (Pin, error) {
	for pin, pinDef := range definedPins {
		for _,name := range pinDef.hwPinRefs {
			if name == cname {
				return pin, nil
			}
		}
	}
	return Pin(0), errors.New(fmt.Sprintf("Could not find a pin called %s", cname))
}

// Set error checking. This should be called before pin assignments.
func SetErrorChecking(check bool) {
	errorChecking = check
}

// Set the mode of a pin. Analogous to Arduino pin mode.
func PinMode(pin Pin, mode PinIOMode) (e error) {
	if errorChecking {
		if e = assertDriver(); e != nil {
			return
		}

		pd := definedPins[pin]
		if pd == nil {
			return errors.New(fmt.Sprintf("Pin %d is not defined by the current driver", pin))
		}

		if e = checkPinMode(mode, pd); e != nil {
			return
		}

		// assign this pin
		assignedPins[pin] = &assignedPin{pinDef: pd, pinIOMode: mode}
	}

	return driver.PinMode(pin, mode)
}

func checkPinMode(mode PinIOMode, pd *PinDef) (e error) {
	ok := false
	switch mode {
	case INPUT:
		ok = pd.HasCapability(CAP_INPUT) || pd.HasCapability(CAP_ANALOG_IN)
	case OUTPUT:
		ok = pd.HasCapability(CAP_OUTPUT)
	case INPUT_PULLUP:
		ok = pd.HasCapability(CAP_INPUT_PULLUP)
	case INPUT_PULLDOWN:
		ok = pd.HasCapability(CAP_INPUT_PULLDOWN)
	}
	if ok {
		return nil
	}
	return errors.New(fmt.Sprintf("Pin %d can't be set to mode %s because it does not support that capability", pd.pin, mode.String()))
}

// Write a value to a digital pin
func DigitalWrite(pin Pin, value int) (e error) {
	if errorChecking {
		if e = assertDriver(); e != nil {
			return
		}

		a := assignedPins[pin]
		if a == nil {
			return errors.New(fmt.Sprintf("DigitalWrite: pin %d mode has not been set", pin))
		}
		if a.pinIOMode != OUTPUT {
			return errors.New(fmt.Sprintf("DigitalWrite: pin %d mode is not set for output", pin))
		}
	}

	return driver.DigitalWrite(pin, value)
}

// Read a value from a digital pin
func DigitalRead(pin Pin) (result int, e error) {
	if errorChecking {
		if e = assertDriver(); e != nil {
			return 0, e
		}

		a := assignedPins[pin]
		if a == nil {
			e = errors.New(fmt.Sprintf("DigitalRead: pin %d mode has not been set", pin))
			return
		}
		if a.pinIOMode != INPUT && a.pinIOMode != INPUT_PULLUP && a.pinIOMode != INPUT_PULLDOWN {
			e = errors.New(fmt.Sprintf("DigitalRead: pin %d mode is not set for input", pin))
			return
		}
	}

	return driver.DigitalRead(pin)
}

// Read an analog value from a pin. The range of values is hardware driver dependent.
func AnalogRead(pin Pin) (result int, e error) {
	if errorChecking {
		if e = assertDriver(); e != nil {
			return
		}

		a := assignedPins[pin]
		if a == nil {
			e = errors.New(fmt.Sprintf("AnalogRead: pin %d mode has not been set", pin))
			return
		}
		if a.pinIOMode != INPUT && a.pinIOMode != INPUT_PULLUP {
			e = errors.New(fmt.Sprintf("AnalogRead: pin %d mode is not set for input", pin))
			return
		}
	}

	return driver.AnalogRead(pin)
}

// Write an analog value. The interpretation is hardware dependent, but is
// generally implemented using PWM.
func AnalogWrite(pin Pin, value int) (e error) {
	if errorChecking {
		if e = assertDriver(); e != nil {
			return
		}

		a := assignedPins[pin]
		if a == nil {
			return errors.New(fmt.Sprintf("AnalogWrite: pin %d mode has not been set", pin))
		}
		if a.pinIOMode != OUTPUT {
			return errors.New(fmt.Sprintf("AnalogWrite: pin %d mode is not set for output", pin))
		}
	}

	return driver.AnalogWrite(pin, value)
}

// Delay execution by the specified number of milliseconds. This is a helper
// function for similarity with Arduino. It is implemented using standard go
// time package.
func Delay(duration int) {
	time.Sleep(time.Duration(duration) * time.Millisecond)
}

// Delay execution by the specified number of microseconds. This is a helper
// function for similarity with Arduino. It is implemented using standard go
// time package
func DelayMicroseconds(duration int) {
	time.Sleep(time.Duration(duration) * time.Microsecond)
}

// @todo DebugPinMap: sort
func DebugPinMap() {
	fmt.Println("HardwarePinMap:")
	for key, val := range definedPins {
		fmt.Printf("Pin %d: %s\n", key, val.String())
	}
	fmt.Printf("\n")
}

// The approximate mapping of Arduino shiftOut, this shifts a byte out on the
// data pin, pulsing the clock pin high and then low.
func ShiftOut(dataPin Pin, clockPin Pin, value uint, order BitShiftOrder) error {
	return ShiftOutSize(dataPin, clockPin, value, order, 8)
}

// More generic version of ShiftOut which shifts out n of data from value. The
// value shifted out is always the lowest n bits of the value, but 'order'
// determines whether the msb or lsb from that value are shifted first
func ShiftOutSize(dataPin Pin, clockPin Pin, value uint, order BitShiftOrder, n uint) error {
	bit := uint(0)
	v := value
	mask := uint(1) << (n - 1)
	for i := uint(0); i < n; i++ {
		// get the next bit
		if order == LSBFIRST {
			bit = v & 1
			v = v >> 1
		} else {
			bit = v & mask
			if bit != 0 {
				bit = 1
			}
			v = v << 1
		}
		// write to data pin
		e := DigitalWrite(dataPin, int(bit))
		if e != nil {
			return e
		}
		// pulse clock high and then low
		e = DigitalWrite(clockPin, HIGH)
		if e != nil {
			return e
		}
		DigitalWrite(clockPin, LOW)
	}
	return nil
}

// def toggle(gpio_pin):
//   """ Toggles the state of the given digital pin. """
//   assert (gpio_pin in GPIO), "*Invalid GPIO pin: '%s'" % gpio_pin
//   _xorReg(GPIO[gpio_pin][0]+GPIO_DATAOUT, GPIO[gpio_pin][1])

// def pinState(gpio_pin):
//   """ Returns the state of a digital pin if it is configured as
//       an output. Returns None if it is configuredas an input. """
//   assert (gpio_pin in GPIO), "*Invalid GPIO pin: '%s'" % gpio_pin
//   if (_getReg(GPIO[gpio_pin][0]+GPIO_OE) & GPIO[gpio_pin][1]):
//     return None
//   if (_getReg(GPIO[gpio_pin][0]+GPIO_DATAOUT) & GPIO[gpio_pin][1]):
//     return HIGH
//   return LOW

// @todo Implement other core Arduino function equivalents:
//	AnalogReference
//	Tone
//	NoTone
//	ShiftOut
//	ShiftIn
//	PulseIn
//	Millis
//	Micros
//	RandomSeed
//	Random
//	AttachInterupt
//	DetachInterupt

// This is the interface that hardware drivers implement.
type HardwareDriver interface {
	// Initialise the driver after creation
	Init() (e error)

	// Set mode of a pin
	PinMode(pin Pin, mode PinIOMode) (e error)

	// Write digital output
	DigitalWrite(pin Pin, value int) error

	// Read digital input
	DigitalRead(Pin) (int, error)

	// PWM write
	AnalogWrite(pin Pin, value int) error

	// Analog input. Resolution is device dependent.
	AnalogRead(pin Pin) (int, error)

	// Return the pin map for the driver, listing all supported pins and their capabilities
	PinMap() (pinMap HardwarePinMap)

	// Close the driver before destruction
	Close()
}
<|endoftext|>


<commit_before>package middleware

import (
	"github.com/forease/i18n"
	"github.com/go-martini/martini"
	"net/http"
	"net/url"
	"strings"
	"sync"
)

const (
	_VERSION              = "0.0.1"
	_LANGUAGE_COOKIE_NAME = "_language"
	_LANGUAGE_PARAM_NAME  = "_language"
	_DEFAULT_DOMAIN       = "martini"
	_DEFAULT_DIRECTORY    = "locale"
	_DEFAULT_LANGUAGE     = "zh_CN"
	_DEFAULT_TMPL_NAME    = "trans"
)

var Locale *i18n.Locale

func Version() string {
	return _VERSION
}

// Options represents a struct for specifying configuration options for the i18n middleware.
type I18nOptions struct {
	Domain      string
	Directory   string
	ZipData     []byte
	DefaultLang string
	// Suburl of path. Default is empty.
	SubURL     string
	CookieName string
	// Name of language parameter name in URL. Default is "lang".
	Parameter string
	// Redirect when user uses get parameter to specify language.
	Redirect bool
	// Name that maps into template variable. Default is "i18n".
	TmplName string
	Inited   bool
}

func prepareOptions(options []I18nOptions) I18nOptions {
	var opt I18nOptions
	if len(options) > 0 {
		opt = options[0]
	}

	opt.SubURL = strings.TrimSuffix(opt.SubURL, "/")
	if len(opt.Domain) == 0 {
		opt.Domain = _DEFAULT_DOMAIN
	}
	if len(opt.Directory) == 0 {
		opt.Directory = _DEFAULT_DIRECTORY
	}
	if len(opt.DefaultLang) == 0 {
		opt.DefaultLang = _DEFAULT_LANGUAGE
	}
	if len(opt.CookieName) == 0 {
		opt.CookieName = _LANGUAGE_COOKIE_NAME
	}
	if len(opt.Parameter) == 0 {
		opt.Parameter = _LANGUAGE_PARAM_NAME
	}
	if len(opt.TmplName) == 0 {
		opt.TmplName = _DEFAULT_TMPL_NAME
	}
	if !opt.Redirect {
		opt.Redirect = true
	}

	return opt
}

func initLocale(opt I18nOptions) {
	var once sync.Once
	onceBody := func() {
		if !i18n.IsExist(opt.DefaultLang) {
			i18n.SetMessage(opt.DefaultLang, opt.Directory+"/locale_"+toLocale(opt.DefaultLang, false)+".ini")
		}
	}
	if !opt.Inited {
		once.Do(onceBody)
	}
}

// I18n is a middleware provides localization layer for your application.
// Paramenter langs must be in the form of "en-US", "zh-CN", etc.
// Otherwise it may not recognize browser input.
func I18n(options ...I18nOptions) martini.Handler {
	return func(res http.ResponseWriter, req *http.Request, c *Context) {
		isNeedRedir := false
		hasCookie := false
		opt := prepareOptions(options)
		initLocale(opt)
		// 1. Check URL arguments.
		lang := req.FormValue(opt.Parameter)
		// 2. Get language information from cookies.
		if len(lang) == 0 {
			var err error
			if lang, err = getCookie(req, opt.CookieName); err == nil {
				hasCookie = true
			}
		} else {
			isNeedRedir = true
		}
		// 3. Get language information from 'Accept-Language'.
		if len(lang) == 0 {
			al := req.Header.Get("Accept-Language")
			if len(al) > 4 {
				al = al[:5] // Only compare first 5 letters.
				lang = al
			}
		}
		// 4. Default language is the first element in the list.
		if len(lang) == 0 {
			lang = opt.DefaultLang
			isNeedRedir = false
		}

		language := toLocale(lang, false)
		if language != opt.DefaultLang && !i18n.IsExist(lang) {
			err := i18n.SetMessage(language, opt.Directory+"/locale_"+language+".ini")
			if err != nil {
				language = opt.DefaultLang
			}
		}

		// Save language information in cookies.
		if !hasCookie {
			setCookie(res, opt.CookieName, language, 1<<31-1, "/"+strings.TrimPrefix(opt.SubURL, "/"))
		}

		Locale = &i18n.Locale{Lang: language}
		c.Set("i18n", Locale)
		if opt.Redirect && isNeedRedir {
			location := opt.SubURL + req.RequestURI[:strings.Index(req.RequestURI, "?")]
			http.Redirect(res, req, location, http.StatusFound)
		}
	}
}

func Tr(format string, args ...interface{}) string {
	return Locale.Tr(format, args...)
}

// GetCookie returns given cookie value from request header.
func getCookie(req *http.Request, name string) (string, error) {
	cookie, err := req.Cookie(name)
	if err != nil {
		return "", err
	}
	return url.QueryUnescape(cookie.Value)
}

// SetCookie sets given cookie value to response header.
func setCookie(resp http.ResponseWriter, name string, value string, others ...interface{}) {
	cookie := http.Cookie{}
	cookie.Name = name
	cookie.Value = url.QueryEscape(value)

	if len(others) > 0 {
		switch v := others[0].(type) {
		case int:
			cookie.MaxAge = v
		case int64:
			cookie.MaxAge = int(v)
		case int32:
			cookie.MaxAge = int(v)
		}
	}

	// default "/"
	if len(others) > 1 {
		if v, ok := others[1].(string); ok && len(v) > 0 {
			cookie.Path = v
		}
	} else {
		cookie.Path = "/"
	}

	// default empty
	if len(others) > 2 {
		if v, ok := others[2].(string); ok && len(v) > 0 {
			cookie.Domain = v
		}
	}

	// default empty
	if len(others) > 3 {
		switch v := others[3].(type) {
		case bool:
			cookie.Secure = v
		default:
			if others[3] != nil {
				cookie.Secure = true
			}
		}
	}

	// default false. for session cookie default true
	if len(others) > 4 {
		if v, ok := others[4].(bool); ok && v {
			cookie.HttpOnly = true
		}
	}

	resp.Header().Add("Set-Cookie", cookie.String())
}

// Turns a language name (en-us) into a locale name (en_US). If 'to_lower' is
// True, the last component is lower-cased (en_us).
func toLocale(language string, to_lower bool) string {
	language = strings.Replace(language, "_", "-", -1)

	if p := strings.Index(language, "-"); p >= 0 {
		if to_lower {
			return strings.ToLower(language[:p]) + "_" + strings.ToLower(language[p+1:])
		} else {
			//# Get correct locale for sr-latn
			if len(language[p+1:]) > 2 {
				return strings.ToLower(language[:p]) + "_" + strings.ToUpper(string(language[p+1])) + strings.ToLower(language[p+2:])
			}
			return strings.ToLower(language[:p]) + "_" + strings.ToUpper(language[p+1:])
		}
	} else {
		return strings.ToLower(language)
	}
}

// Turns a locale name (en_US) into a language name (en-us).
func toLanguage(locale string) string {
	if p := strings.Index(locale, "_"); p >= 0 {
		return strings.ToLower(locale[:p]) + "-" + strings.ToLower(locale[p+1:])
	} else {
		return strings.ToLower(locale)
	}
}
<commit_msg>add error message<commit_after>package middleware

import (
	. "github.com/forease/ebase"
	"github.com/forease/i18n"
	"github.com/go-martini/martini"
	"net/http"
	"net/url"
	"strings"
	"sync"
)

const (
	_VERSION              = "0.0.1"
	_LANGUAGE_COOKIE_NAME = "_language"
	_LANGUAGE_PARAM_NAME  = "_language"
	_DEFAULT_DOMAIN       = "martini"
	_DEFAULT_DIRECTORY    = "locale"
	_DEFAULT_LANGUAGE     = "zh_CN"
	_DEFAULT_TMPL_NAME    = "trans"
)

var Locale *i18n.Locale

func Version() string {
	return _VERSION
}

// Options represents a struct for specifying configuration options for the i18n middleware.
type I18nOptions struct {
	Domain      string
	Directory   string
	ZipData     []byte
	DefaultLang string
	// Suburl of path. Default is empty.
	SubURL     string
	CookieName string
	// Name of language parameter name in URL. Default is "lang".
	Parameter string
	// Redirect when user uses get parameter to specify language.
	Redirect bool
	// Name that maps into template variable. Default is "i18n".
	TmplName string
	Inited   bool
}

func prepareOptions(options []I18nOptions) I18nOptions {
	var opt I18nOptions
	if len(options) > 0 {
		opt = options[0]
	}

	opt.SubURL = strings.TrimSuffix(opt.SubURL, "/")
	if len(opt.Domain) == 0 {
		opt.Domain = _DEFAULT_DOMAIN
	}
	if len(opt.Directory) == 0 {
		opt.Directory = _DEFAULT_DIRECTORY
	}
	if len(opt.DefaultLang) == 0 {
		opt.DefaultLang = _DEFAULT_LANGUAGE
	}
	if len(opt.CookieName) == 0 {
		opt.CookieName = _LANGUAGE_COOKIE_NAME
	}
	if len(opt.Parameter) == 0 {
		opt.Parameter = _LANGUAGE_PARAM_NAME
	}
	if len(opt.TmplName) == 0 {
		opt.TmplName = _DEFAULT_TMPL_NAME
	}
	if !opt.Redirect {
		opt.Redirect = true
	}

	return opt
}

func initLocale(opt I18nOptions) {
	var once sync.Once
	onceBody := func() {
		if !i18n.IsExist(opt.DefaultLang) {
			err := i18n.SetMessage(opt.DefaultLang, opt.Directory+"/locale_"+toLocale(opt.DefaultLang, false)+".ini")
			if err != nil {
				Log.Errorf("initial for %s error %s!", opt.DefaultLang, err)
			}
		}
	}
	if !opt.Inited {
		once.Do(onceBody)
	}
}

// I18n is a middleware provides localization layer for your application.
// Paramenter langs must be in the form of "en-US", "zh-CN", etc.
// Otherwise it may not recognize browser input.
func I18n(options ...I18nOptions) martini.Handler {
	return func(res http.ResponseWriter, req *http.Request, c *Context) {
		isNeedRedir := false
		hasCookie := false
		opt := prepareOptions(options)
		initLocale(opt)
		// 1. Check URL arguments.
		lang := req.FormValue(opt.Parameter)
		// 2. Get language information from cookies.
		if len(lang) == 0 {
			var err error
			if lang, err = getCookie(req, opt.CookieName); err == nil {
				hasCookie = true
			} else {
				Log.Error("getCookie error", err)
			}
		} else {
			isNeedRedir = true
		}
		// 3. Get language information from 'Accept-Language'.
		if len(lang) == 0 {
			al := req.Header.Get("Accept-Language")
			if len(al) > 4 {
				al = al[:5] // Only compare first 5 letters.
				lang = al
			}
		}
		// 4. Default language is the first element in the list.
		if len(lang) == 0 {
			lang = opt.DefaultLang
			isNeedRedir = false
		}

		language := toLocale(lang, false)
		if language != opt.DefaultLang && !i18n.IsExist(lang) {
			err := i18n.SetMessage(language, opt.Directory+"/locale_"+language+".ini")
			if err != nil {
				Log.Errorf("initial for %s error %s!", language, err)
				language = opt.DefaultLang
			}
		}

		// Save language information in cookies.
		if !hasCookie {
			setCookie(res, opt.CookieName, language, 1<<31-1, "/"+strings.TrimPrefix(opt.SubURL, "/"))
		}

		Locale = &i18n.Locale{Lang: language}
		c.Set("i18n", Locale)
		if opt.Redirect && isNeedRedir {
			location := opt.SubURL + req.RequestURI[:strings.Index(req.RequestURI, "?")]
			http.Redirect(res, req, location, http.StatusFound)
		}
	}
}

func Tr(format string, args ...interface{}) string {
	return Locale.Tr(format, args...)
}

// GetCookie returns given cookie value from request header.
func getCookie(req *http.Request, name string) (string, error) {
	cookie, err := req.Cookie(name)
	if err != nil {
		return "", err
	}
	return url.QueryUnescape(cookie.Value)
}

// SetCookie sets given cookie value to response header.
func setCookie(resp http.ResponseWriter, name string, value string, others ...interface{}) {
	cookie := http.Cookie{}
	cookie.Name = name
	cookie.Value = url.QueryEscape(value)

	if len(others) > 0 {
		switch v := others[0].(type) {
		case int:
			cookie.MaxAge = v
		case int64:
			cookie.MaxAge = int(v)
		case int32:
			cookie.MaxAge = int(v)
		}
	}

	// default "/"
	if len(others) > 1 {
		if v, ok := others[1].(string); ok && len(v) > 0 {
			cookie.Path = v
		}
	} else {
		cookie.Path = "/"
	}

	// default empty
	if len(others) > 2 {
		if v, ok := others[2].(string); ok && len(v) > 0 {
			cookie.Domain = v
		}
	}

	// default empty
	if len(others) > 3 {
		switch v := others[3].(type) {
		case bool:
			cookie.Secure = v
		default:
			if others[3] != nil {
				cookie.Secure = true
			}
		}
	}

	// default false. for session cookie default true
	if len(others) > 4 {
		if v, ok := others[4].(bool); ok && v {
			cookie.HttpOnly = true
		}
	}

	resp.Header().Add("Set-Cookie", cookie.String())
}

// Turns a language name (en-us) into a locale name (en_US). If 'to_lower' is
// True, the last component is lower-cased (en_us).
func toLocale(language string, to_lower bool) string {
	language = strings.Replace(language, "_", "-", -1)

	if p := strings.Index(language, "-"); p >= 0 {
		if to_lower {
			return strings.ToLower(language[:p]) + "_" + strings.ToLower(language[p+1:])
		} else {
			//# Get correct locale for sr-latn
			if len(language[p+1:]) > 2 {
				return strings.ToLower(language[:p]) + "_" + strings.ToUpper(string(language[p+1])) + strings.ToLower(language[p+2:])
			}
			return strings.ToLower(language[:p]) + "_" + strings.ToUpper(language[p+1:])
		}
	} else {
		return strings.ToLower(language)
	}
}

// Turns a locale name (en_US) into a language name (en-us).
func toLanguage(locale string) string {
	if p := strings.Index(locale, "_"); p >= 0 {
		return strings.ToLower(locale[:p]) + "-" + strings.ToLower(locale[p+1:])
	} else {
		return strings.ToLower(locale)
	}
}
<|endoftext|>