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

package api

import (
	"fmt"
	"net/http"
	"strconv"

	"github.com/golang/glog"
	info "github.com/google/cadvisor/info/v1"
	"github.com/google/cadvisor/info/v2"
	"github.com/google/cadvisor/manager"
)

const (
	containersApi    = "containers"
	subcontainersApi = "subcontainers"
	machineApi       = "machine"
	dockerApi        = "docker"
	summaryApi       = "summary"
	statsApi         = "stats"
	specApi          = "spec"
	eventsApi        = "events"
	storageApi       = "storage"
	attributesApi    = "attributes"
	versionApi       = "version"
)

// Interface for a cAdvisor API version
type ApiVersion interface {
	// Returns the version string.
	Version() string

	// List of supported API endpoints.
	SupportedRequestTypes() []string

	// Handles a request. The second argument is the parameters after /api/<version>/<endpoint>
	HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error
}

// Gets all supported API versions.
func getApiVersions() []ApiVersion {
	v1_0 := &version1_0{}
	v1_1 := newVersion1_1(v1_0)
	v1_2 := newVersion1_2(v1_1)
	v1_3 := newVersion1_3(v1_2)
	v2_0 := newVersion2_0()

	return []ApiVersion{v1_0, v1_1, v1_2, v1_3, v2_0}

}

// API v1.0

type version1_0 struct {
}

func (self *version1_0) Version() string {
	return "v1.0"
}

func (self *version1_0) SupportedRequestTypes() []string {
	return []string{containersApi, machineApi}
}

func (self *version1_0) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
	switch requestType {
	case machineApi:
		glog.V(2).Infof("Api - Machine")

		// Get the MachineInfo
		machineInfo, err := m.GetMachineInfo()
		if err != nil {
			return err
		}

		err = writeResult(machineInfo, w)
		if err != nil {
			return err
		}
	case containersApi:
		containerName := getContainerName(request)
		glog.V(2).Infof("Api - Container(%s)", containerName)

		// Get the query request.
		query, err := getContainerInfoRequest(r.Body)
		if err != nil {
			return err
		}

		// Get the container.
		cont, err := m.GetContainerInfo(containerName, query)
		if err != nil {
			return fmt.Errorf("failed to get container %q with error: %s", containerName, err)
		}

		// Only output the container as JSON.
		err = writeResult(cont, w)
		if err != nil {
			return err
		}
	default:
		return fmt.Errorf("unknown request type %q", requestType)
	}
	return nil
}

// API v1.1

type version1_1 struct {
	baseVersion *version1_0
}

// v1.1 builds on v1.0.
func newVersion1_1(v *version1_0) *version1_1 {
	return &version1_1{
		baseVersion: v,
	}
}

func (self *version1_1) Version() string {
	return "v1.1"
}

func (self *version1_1) SupportedRequestTypes() []string {
	return append(self.baseVersion.SupportedRequestTypes(), subcontainersApi)
}

func (self *version1_1) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
	switch requestType {
	case subcontainersApi:
		containerName := getContainerName(request)
		glog.V(2).Infof("Api - Subcontainers(%s)", containerName)

		// Get the query request.
		query, err := getContainerInfoRequest(r.Body)
		if err != nil {
			return err
		}

		// Get the subcontainers.
		containers, err := m.SubcontainersInfo(containerName, query)
		if err != nil {
			return fmt.Errorf("failed to get subcontainers for container %q with error: %s", containerName, err)
		}

		// Only output the containers as JSON.
		err = writeResult(containers, w)
		if err != nil {
			return err
		}
		return nil
	default:
		return self.baseVersion.HandleRequest(requestType, request, m, w, r)
	}
}

// API v1.2

type version1_2 struct {
	baseVersion *version1_1
}

// v1.2 builds on v1.1.
func newVersion1_2(v *version1_1) *version1_2 {
	return &version1_2{
		baseVersion: v,
	}
}

func (self *version1_2) Version() string {
	return "v1.2"
}

func (self *version1_2) SupportedRequestTypes() []string {
	return append(self.baseVersion.SupportedRequestTypes(), dockerApi)
}

func (self *version1_2) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
	switch requestType {
	case dockerApi:
		glog.V(2).Infof("Api - Docker(%v)", request)

		// Get the query request.
		query, err := getContainerInfoRequest(r.Body)
		if err != nil {
			return err
		}

		var containers map[string]info.ContainerInfo
		// map requests for "docker/" to "docker"
		if len(request) == 1 && len(request[0]) == 0 {
			request = request[:0]
		}
		switch len(request) {
		case 0:
			// Get all Docker containers.
			containers, err = m.AllDockerContainers(query)
			if err != nil {
				return fmt.Errorf("failed to get all Docker containers with error: %v", err)
			}
		case 1:
			// Get one Docker container.
			var cont info.ContainerInfo
			cont, err = m.DockerContainer(request[0], query)
			if err != nil {
				return fmt.Errorf("failed to get Docker container %q with error: %v", request[0], err)
			}
			containers = map[string]info.ContainerInfo{
				cont.Name: cont,
			}
		default:
			return fmt.Errorf("unknown request for Docker container %v", request)
		}

		// Only output the containers as JSON.
		err = writeResult(containers, w)
		if err != nil {
			return err
		}
		return nil
	default:
		return self.baseVersion.HandleRequest(requestType, request, m, w, r)
	}
}

// API v1.3

type version1_3 struct {
	baseVersion *version1_2
}

// v1.3 builds on v1.2.
func newVersion1_3(v *version1_2) *version1_3 {
	return &version1_3{
		baseVersion: v,
	}
}

func (self *version1_3) Version() string {
	return "v1.3"
}

func (self *version1_3) SupportedRequestTypes() []string {
	return append(self.baseVersion.SupportedRequestTypes(), eventsApi)
}

func (self *version1_3) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
	switch requestType {
	case eventsApi:
		query, eventsFromAllTime, err := getEventRequest(r)
		if err != nil {
			return err
		}
		glog.V(2).Infof("Api - Events(%v)", query)
		if eventsFromAllTime {
			pastEvents, err := m.GetPastEvents(query)
			if err != nil {
				return err
			}
			return writeResult(pastEvents, w)
		}
		eventChannel, err := m.WatchForEvents(query)
		if err != nil {
			return err
		}
		return streamResults(eventChannel, w, r, m)
	default:
		return self.baseVersion.HandleRequest(requestType, request, m, w, r)
	}
}

// API v2.0

// v2.0 builds on v1.3
type version2_0 struct {
}

func newVersion2_0() *version2_0 {
	return &version2_0{}
}

func (self *version2_0) Version() string {
	return "v2.0"
}

func (self *version2_0) SupportedRequestTypes() []string {
	return []string{versionApi, attributesApi, machineApi, summaryApi, statsApi, specApi, storageApi}
}

func (self *version2_0) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
	opt, err := getRequestOptions(r)
	if err != nil {
		return err
	}
	switch requestType {
	case versionApi:
		glog.V(2).Infof("Api - Version")
		versionInfo, err := m.GetVersionInfo()
		if err != nil {
			return err
		}
		return writeResult(versionInfo.CadvisorVersion, w)
	case attributesApi:
		glog.V(2).Info("Api - Attributes")

		machineInfo, err := m.GetMachineInfo()
		if err != nil {
			return err
		}
		versionInfo, err := m.GetVersionInfo()
		if err != nil {
			return err
		}
		info := v2.GetAttributes(machineInfo, versionInfo)
		return writeResult(info, w)
	case machineApi:
		glog.V(2).Info("Api - Machine")

		// TODO(rjnagal): Move machineInfo from v1.
		machineInfo, err := m.GetMachineInfo()
		if err != nil {
			return err
		}
		return writeResult(machineInfo, w)
	case summaryApi:
		containerName := getContainerName(request)
		glog.V(2).Infof("Api - Summary for container %q, options %+v", containerName, opt)

		stats, err := m.GetDerivedStats(containerName, opt)
		if err != nil {
			return err
		}
		return writeResult(stats, w)
	case statsApi:
		name := getContainerName(request)
		glog.V(2).Infof("Api - Stats: Looking for stats for container %q, options %+v", name, opt)
		conts, err := m.GetRequestedContainersInfo(name, opt)
		if err != nil {
			return err
		}
		contStats := make(map[string][]v2.ContainerStats, 0)
		for name, cont := range conts {
			contStats[name] = convertStats(cont)
		}
		return writeResult(contStats, w)
	case specApi:
		containerName := getContainerName(request)
		glog.V(2).Infof("Api - Spec for container %q, options %+v", containerName, opt)
		specs, err := m.GetContainerSpec(containerName, opt)
		if err != nil {
			return err
		}
		return writeResult(specs, w)
	case storageApi:
		var err error
		fi := []v2.FsInfo{}
		label := r.URL.Query().Get("label")
		if len(label) == 0 {
			// Get all global filesystems info.
			fi, err = m.GetFsInfo("")
			if err != nil {
				return err
			}
		} else {
			// Get a specific label.
			fi, err = m.GetFsInfo(label)
			if err != nil {
				return err
			}
		}
		return writeResult(fi, w)
	default:
		return fmt.Errorf("unknown request type %q", requestType)
	}
}

func convertStats(cont *info.ContainerInfo) []v2.ContainerStats {
	stats := []v2.ContainerStats{}
	for _, val := range cont.Stats {
		stat := v2.ContainerStats{
			Timestamp:     val.Timestamp,
			HasCpu:        cont.Spec.HasCpu,
			HasMemory:     cont.Spec.HasMemory,
			HasNetwork:    cont.Spec.HasNetwork,
			HasFilesystem: cont.Spec.HasFilesystem,
			HasDiskIo:     cont.Spec.HasDiskIo,
		}
		if stat.HasCpu {
			stat.Cpu = val.Cpu
		}
		if stat.HasMemory {
			stat.Memory = val.Memory
		}
		if stat.HasNetwork {
			// TODO(rjnagal): Return stats about all network interfaces.
			stat.Network = append(stat.Network, val.Network)
		}
		if stat.HasFilesystem {
			stat.Filesystem = val.Filesystem
		}
		if stat.HasDiskIo {
			stat.DiskIo = val.DiskIo
		}
		// TODO(rjnagal): Handle load stats.
		stats = append(stats, stat)
	}
	return stats
}

func getRequestOptions(r *http.Request) (v2.RequestOptions, error) {
	supportedTypes := map[string]bool{
		v2.TypeName:   true,
		v2.TypeDocker: true,
	}
	// fill in the defaults.
	opt := v2.RequestOptions{
		IdType:    v2.TypeName,
		Count:     64,
		Recursive: false,
	}
	idType := r.URL.Query().Get("type")
	if len(idType) != 0 {
		if !supportedTypes[idType] {
			return opt, fmt.Errorf("unknown 'type' %q", idType)
		}
		opt.IdType = idType
	}
	count := r.URL.Query().Get("count")
	if len(count) != 0 {
		n, err := strconv.ParseUint(count, 10, 32)
		if err != nil {
			return opt, fmt.Errorf("failed to parse 'count' option: %v", count)
		}
		opt.Count = int(n)
	}
	recursive := r.URL.Query().Get("recursive")
	if recursive == "true" {
		opt.Recursive = true
	}
	return opt, nil
}
<commit_msg>Add events endpoint to v2.0.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package api

import (
	"fmt"
	"net/http"
	"strconv"

	"github.com/golang/glog"
	info "github.com/google/cadvisor/info/v1"
	"github.com/google/cadvisor/info/v2"
	"github.com/google/cadvisor/manager"
)

const (
	containersApi    = "containers"
	subcontainersApi = "subcontainers"
	machineApi       = "machine"
	dockerApi        = "docker"
	summaryApi       = "summary"
	statsApi         = "stats"
	specApi          = "spec"
	eventsApi        = "events"
	storageApi       = "storage"
	attributesApi    = "attributes"
	versionApi       = "version"
)

// Interface for a cAdvisor API version
type ApiVersion interface {
	// Returns the version string.
	Version() string

	// List of supported API endpoints.
	SupportedRequestTypes() []string

	// Handles a request. The second argument is the parameters after /api/<version>/<endpoint>
	HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error
}

// Gets all supported API versions.
func getApiVersions() []ApiVersion {
	v1_0 := &version1_0{}
	v1_1 := newVersion1_1(v1_0)
	v1_2 := newVersion1_2(v1_1)
	v1_3 := newVersion1_3(v1_2)
	v2_0 := newVersion2_0()

	return []ApiVersion{v1_0, v1_1, v1_2, v1_3, v2_0}

}

// API v1.0

type version1_0 struct {
}

func (self *version1_0) Version() string {
	return "v1.0"
}

func (self *version1_0) SupportedRequestTypes() []string {
	return []string{containersApi, machineApi}
}

func (self *version1_0) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
	switch requestType {
	case machineApi:
		glog.V(2).Infof("Api - Machine")

		// Get the MachineInfo
		machineInfo, err := m.GetMachineInfo()
		if err != nil {
			return err
		}

		err = writeResult(machineInfo, w)
		if err != nil {
			return err
		}
	case containersApi:
		containerName := getContainerName(request)
		glog.V(2).Infof("Api - Container(%s)", containerName)

		// Get the query request.
		query, err := getContainerInfoRequest(r.Body)
		if err != nil {
			return err
		}

		// Get the container.
		cont, err := m.GetContainerInfo(containerName, query)
		if err != nil {
			return fmt.Errorf("failed to get container %q with error: %s", containerName, err)
		}

		// Only output the container as JSON.
		err = writeResult(cont, w)
		if err != nil {
			return err
		}
	default:
		return fmt.Errorf("unknown request type %q", requestType)
	}
	return nil
}

// API v1.1

type version1_1 struct {
	baseVersion *version1_0
}

// v1.1 builds on v1.0.
func newVersion1_1(v *version1_0) *version1_1 {
	return &version1_1{
		baseVersion: v,
	}
}

func (self *version1_1) Version() string {
	return "v1.1"
}

func (self *version1_1) SupportedRequestTypes() []string {
	return append(self.baseVersion.SupportedRequestTypes(), subcontainersApi)
}

func (self *version1_1) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
	switch requestType {
	case subcontainersApi:
		containerName := getContainerName(request)
		glog.V(2).Infof("Api - Subcontainers(%s)", containerName)

		// Get the query request.
		query, err := getContainerInfoRequest(r.Body)
		if err != nil {
			return err
		}

		// Get the subcontainers.
		containers, err := m.SubcontainersInfo(containerName, query)
		if err != nil {
			return fmt.Errorf("failed to get subcontainers for container %q with error: %s", containerName, err)
		}

		// Only output the containers as JSON.
		err = writeResult(containers, w)
		if err != nil {
			return err
		}
		return nil
	default:
		return self.baseVersion.HandleRequest(requestType, request, m, w, r)
	}
}

// API v1.2

type version1_2 struct {
	baseVersion *version1_1
}

// v1.2 builds on v1.1.
func newVersion1_2(v *version1_1) *version1_2 {
	return &version1_2{
		baseVersion: v,
	}
}

func (self *version1_2) Version() string {
	return "v1.2"
}

func (self *version1_2) SupportedRequestTypes() []string {
	return append(self.baseVersion.SupportedRequestTypes(), dockerApi)
}

func (self *version1_2) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
	switch requestType {
	case dockerApi:
		glog.V(2).Infof("Api - Docker(%v)", request)

		// Get the query request.
		query, err := getContainerInfoRequest(r.Body)
		if err != nil {
			return err
		}

		var containers map[string]info.ContainerInfo
		// map requests for "docker/" to "docker"
		if len(request) == 1 && len(request[0]) == 0 {
			request = request[:0]
		}
		switch len(request) {
		case 0:
			// Get all Docker containers.
			containers, err = m.AllDockerContainers(query)
			if err != nil {
				return fmt.Errorf("failed to get all Docker containers with error: %v", err)
			}
		case 1:
			// Get one Docker container.
			var cont info.ContainerInfo
			cont, err = m.DockerContainer(request[0], query)
			if err != nil {
				return fmt.Errorf("failed to get Docker container %q with error: %v", request[0], err)
			}
			containers = map[string]info.ContainerInfo{
				cont.Name: cont,
			}
		default:
			return fmt.Errorf("unknown request for Docker container %v", request)
		}

		// Only output the containers as JSON.
		err = writeResult(containers, w)
		if err != nil {
			return err
		}
		return nil
	default:
		return self.baseVersion.HandleRequest(requestType, request, m, w, r)
	}
}

// API v1.3

type version1_3 struct {
	baseVersion *version1_2
}

// v1.3 builds on v1.2.
func newVersion1_3(v *version1_2) *version1_3 {
	return &version1_3{
		baseVersion: v,
	}
}

func (self *version1_3) Version() string {
	return "v1.3"
}

func (self *version1_3) SupportedRequestTypes() []string {
	return append(self.baseVersion.SupportedRequestTypes(), eventsApi)
}

func (self *version1_3) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
	switch requestType {
	case eventsApi:
		return handleEventRequest(m, w, r)
	default:
		return self.baseVersion.HandleRequest(requestType, request, m, w, r)
	}
}

func handleEventRequest(m manager.Manager, w http.ResponseWriter, r *http.Request) error {
	query, eventsFromAllTime, err := getEventRequest(r)
	if err != nil {
		return err
	}
	glog.V(2).Infof("Api - Events(%v)", query)
	if eventsFromAllTime {
		pastEvents, err := m.GetPastEvents(query)
		if err != nil {
			return err
		}
		return writeResult(pastEvents, w)
	}
	eventChannel, err := m.WatchForEvents(query)
	if err != nil {
		return err
	}
	return streamResults(eventChannel, w, r, m)

}

// API v2.0

type version2_0 struct {
}

func newVersion2_0() *version2_0 {
	return &version2_0{}
}

func (self *version2_0) Version() string {
	return "v2.0"
}

func (self *version2_0) SupportedRequestTypes() []string {
	return []string{versionApi, attributesApi, eventsApi, machineApi, summaryApi, statsApi, specApi, storageApi}
}

func (self *version2_0) HandleRequest(requestType string, request []string, m manager.Manager, w http.ResponseWriter, r *http.Request) error {
	opt, err := getRequestOptions(r)
	if err != nil {
		return err
	}
	switch requestType {
	case versionApi:
		glog.V(2).Infof("Api - Version")
		versionInfo, err := m.GetVersionInfo()
		if err != nil {
			return err
		}
		return writeResult(versionInfo.CadvisorVersion, w)
	case attributesApi:
		glog.V(2).Info("Api - Attributes")

		machineInfo, err := m.GetMachineInfo()
		if err != nil {
			return err
		}
		versionInfo, err := m.GetVersionInfo()
		if err != nil {
			return err
		}
		info := v2.GetAttributes(machineInfo, versionInfo)
		return writeResult(info, w)
	case machineApi:
		glog.V(2).Info("Api - Machine")

		// TODO(rjnagal): Move machineInfo from v1.
		machineInfo, err := m.GetMachineInfo()
		if err != nil {
			return err
		}
		return writeResult(machineInfo, w)
	case summaryApi:
		containerName := getContainerName(request)
		glog.V(2).Infof("Api - Summary for container %q, options %+v", containerName, opt)

		stats, err := m.GetDerivedStats(containerName, opt)
		if err != nil {
			return err
		}
		return writeResult(stats, w)
	case statsApi:
		name := getContainerName(request)
		glog.V(2).Infof("Api - Stats: Looking for stats for container %q, options %+v", name, opt)
		conts, err := m.GetRequestedContainersInfo(name, opt)
		if err != nil {
			return err
		}
		contStats := make(map[string][]v2.ContainerStats, 0)
		for name, cont := range conts {
			contStats[name] = convertStats(cont)
		}
		return writeResult(contStats, w)
	case specApi:
		containerName := getContainerName(request)
		glog.V(2).Infof("Api - Spec for container %q, options %+v", containerName, opt)
		specs, err := m.GetContainerSpec(containerName, opt)
		if err != nil {
			return err
		}
		return writeResult(specs, w)
	case storageApi:
		var err error
		fi := []v2.FsInfo{}
		label := r.URL.Query().Get("label")
		if len(label) == 0 {
			// Get all global filesystems info.
			fi, err = m.GetFsInfo("")
			if err != nil {
				return err
			}
		} else {
			// Get a specific label.
			fi, err = m.GetFsInfo(label)
			if err != nil {
				return err
			}
		}
		return writeResult(fi, w)
	case eventsApi:
		return handleEventRequest(m, w, r)
	default:
		return fmt.Errorf("unknown request type %q", requestType)
	}
}

func convertStats(cont *info.ContainerInfo) []v2.ContainerStats {
	stats := []v2.ContainerStats{}
	for _, val := range cont.Stats {
		stat := v2.ContainerStats{
			Timestamp:     val.Timestamp,
			HasCpu:        cont.Spec.HasCpu,
			HasMemory:     cont.Spec.HasMemory,
			HasNetwork:    cont.Spec.HasNetwork,
			HasFilesystem: cont.Spec.HasFilesystem,
			HasDiskIo:     cont.Spec.HasDiskIo,
		}
		if stat.HasCpu {
			stat.Cpu = val.Cpu
		}
		if stat.HasMemory {
			stat.Memory = val.Memory
		}
		if stat.HasNetwork {
			// TODO(rjnagal): Return stats about all network interfaces.
			stat.Network = append(stat.Network, val.Network)
		}
		if stat.HasFilesystem {
			stat.Filesystem = val.Filesystem
		}
		if stat.HasDiskIo {
			stat.DiskIo = val.DiskIo
		}
		// TODO(rjnagal): Handle load stats.
		stats = append(stats, stat)
	}
	return stats
}

func getRequestOptions(r *http.Request) (v2.RequestOptions, error) {
	supportedTypes := map[string]bool{
		v2.TypeName:   true,
		v2.TypeDocker: true,
	}
	// fill in the defaults.
	opt := v2.RequestOptions{
		IdType:    v2.TypeName,
		Count:     64,
		Recursive: false,
	}
	idType := r.URL.Query().Get("type")
	if len(idType) != 0 {
		if !supportedTypes[idType] {
			return opt, fmt.Errorf("unknown 'type' %q", idType)
		}
		opt.IdType = idType
	}
	count := r.URL.Query().Get("count")
	if len(count) != 0 {
		n, err := strconv.ParseUint(count, 10, 32)
		if err != nil {
			return opt, fmt.Errorf("failed to parse 'count' option: %v", count)
		}
		opt.Count = int(n)
	}
	recursive := r.URL.Query().Get("recursive")
	if recursive == "true" {
		opt.Recursive = true
	}
	return opt, nil
}
<|endoftext|>


<commit_before>package bidirpc

import (
	"bytes"
	"errors"
)

type stream struct {
	id             byte
	sessionClosedC <-chan struct{}
	reader         *bytes.Buffer
	inC            chan []byte
	writer         *bytes.Buffer
	outC           chan []byte
}

func newStream(id byte, sessionClosedC <-chan struct{}) *stream {
	s := &stream{
		id:             id,
		sessionClosedC: sessionClosedC,
		inC:            make(chan []byte),
		outC:           make(chan []byte),
	}
	return s
}

func (s *stream) Read(p []byte) (n int, err error) {
	if s.reader == nil || s.reader.Len() == 0 {
		select {
		case <-s.sessionClosedC:
			return 0, errors.New("stream read from a closed session")
		case data := <-s.inC: // only the message body
			s.reader = bytes.NewBuffer(data)
		}
	}
	return s.reader.Read(p)
}

func (s *stream) Write(p []byte) (n int, err error) {
	if s.writer == nil {
		s.writer = bytes.NewBuffer(nil)
		var dummyHeader [4]byte
		s.writer.Write(dummyHeader[:])
	}
	return s.writer.Write(p)
}

func (s *stream) flush() error {
	buffer := s.writer.Bytes()
	bodyLen := s.writer.Len() - 4
	encodeHeader(buffer, s.id, bodyLen)
	select {
	case <-s.sessionClosedC:
		return errors.New("stream flush to a closed session")
	case s.outC <- buffer:
	}

	s.writer.Truncate(4) // for dummy header
	return nil
}
<commit_msg>Reusing stream.writer is not safe<commit_after>package bidirpc

import (
	"bytes"
	"errors"
)

type stream struct {
	id             byte
	sessionClosedC <-chan struct{}
	reader         *bytes.Buffer
	inC            chan []byte
	writer         *bytes.Buffer
	outC           chan []byte
}

func newStream(id byte, sessionClosedC <-chan struct{}) *stream {
	s := &stream{
		id:             id,
		sessionClosedC: sessionClosedC,
		inC:            make(chan []byte),
		outC:           make(chan []byte),
	}
	return s
}

func (s *stream) Read(p []byte) (n int, err error) {
	if s.reader == nil || s.reader.Len() == 0 {
		select {
		case <-s.sessionClosedC:
			return 0, errors.New("stream read from a closed session")
		case data := <-s.inC: // only the message body
			s.reader = bytes.NewBuffer(data)
		}
	}
	return s.reader.Read(p)
}

func (s *stream) Write(p []byte) (n int, err error) {
	if s.writer == nil {
		s.writer = bytes.NewBuffer(nil)
		var dummyHeader [4]byte
		s.writer.Write(dummyHeader[:])
	}
	return s.writer.Write(p)
}

func (s *stream) flush() error {
	buffer := s.writer.Bytes()
	bodyLen := s.writer.Len() - 4
	encodeHeader(buffer, s.id, bodyLen)
	select {
	case <-s.sessionClosedC:
		return errors.New("stream flush to a closed session")
	case s.outC <- buffer:
	}

	s.writer = nil
	return nil
}
<|endoftext|>


<commit_before>// Package stream provides a way to read and write to a synchronous buffered pipe, with multiple reader support.
package stream

import (
	"errors"
	"sync"
)

// ErrUnsupported is returned when an operation is not supported.
var ErrUnsupported = errors.New("unsupported")

// Stream is used to concurrently Write and Read from a File.
type Stream struct {
	mu        sync.Mutex
	b         *broadcaster
	file      File
	fs        FileSystem
	closeOnce onceWithErr
}

// New creates a new Stream from the StdFileSystem with Name "name".
func New(name string) (*Stream, error) {
	return NewStream(name, StdFileSystem)
}

// NewStream creates a new Stream with Name "name" in FileSystem fs.
func NewStream(name string, fs FileSystem) (*Stream, error) {
	f, err := fs.Create(name)
	return newStream(f, fs), err
}

// NewMemStream creates an in-memory stream with no name, and no underlying fs.
// This should replace uses of NewStream("name", NewMemFs()).
// Remove() is unsupported as there is no fs to remove it from.
func NewMemStream() *Stream {
	f := newMemFile("")
	return newStream(f, singletonFs{f})
}

func newStream(file File, fs FileSystem) *Stream {
	return &Stream{
		file: file,
		fs:   fs,
		b:    newBroadcaster(),
	}
}

type singletonFs struct {
	file *memFile
}

func (fs singletonFs) Create(key string) (File, error) { return nil, ErrUnsupported }

func (fs singletonFs) Open(key string) (File, error) { return &memReader{memFile: fs.file}, nil }

func (fs singletonFs) Remove(key string) error { return ErrUnsupported }

// Name returns the name of the underlying File in the FileSystem.
func (s *Stream) Name() string { return s.file.Name() }

// Write writes p to the Stream. It's concurrent safe to be called with Stream's other methods.
func (s *Stream) Write(p []byte) (int, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	n, err := s.file.Write(p)
	s.b.Wrote(n)
	return n, err
}

// Close will close the active stream. This will cause Readers to return EOF once they have
// read the entire stream.
func (s *Stream) Close() error {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.closeOnce.Do(func() (err error) {
		err = s.file.Close()
		s.b.Close()
		return err
	})
}

// Remove will block until the Stream and all its Readers have been Closed,
// at which point it will delete the underlying file. NextReader() will return
// ErrRemoving if called after Remove.
func (s *Stream) Remove() error {
	s.shutdownWithErr(ErrRemoving)
	return s.fs.Remove(s.file.Name())
}

// shutdownWithErr causes NextReader to stop creating new Readers and instead return err, this
// method also blocks until all Readers and the Writer have closed.
func (s *Stream) shutdownWithErr(err error) {
	if err == nil {
		return
	}
	s.b.PreventNewHandles(err) // no new readers can be created, but existing ones can finish, same with the writer
	s.b.WaitForZeroHandles()   // wait for exiting handles to finish up
}

// Cancel signals that this Stream is forcibly ending, NextReader() will fail, existing readers will fail Reads, all Readers & Writer are Closed.
// This call is non-blocking, and Remove() after this call is non-blocking.
func (s *Stream) Cancel() error {
	s.b.Cancel()     // all existing reads are canceled, no new reads will occur, all readers closed
	return s.Close() // all writes are stopped
}

// Shutdown signals that this Stream is not accepting more readers and wait for existing ones to close
// NextReader() will fail, existing readers will fail Reads, all Readers & Writer are Closed.
// This call is non-blocking, and Remove() after this call is non-blocking.
func (s *Stream) Shutdown() error {
	s.b.Shutdown()   // all existing reads are canceled, no new reads will occur, block until all reads are closed
	return s.Close() // all writes are stopped
}

// NextReader will return a concurrent-safe Reader for this stream. Each Reader will
// see a complete and independent view of the stream, and can Read while the stream
// is written to.
func (s *Stream) NextReader() (*Reader, error) {
	return s.b.NewReader(func() (*Reader, error) {
		file, err := s.fs.Open(s.file.Name())
		if err != nil {
			return nil, err
		}
		return &Reader{file: file, s: s}, nil
	})
}
<commit_msg>First close stream, then wait for all readers to close<commit_after>// Package stream provides a way to read and write to a synchronous buffered pipe, with multiple reader support.
package stream

import (
	"errors"
	"sync"
)

// ErrUnsupported is returned when an operation is not supported.
var ErrUnsupported = errors.New("unsupported")

// Stream is used to concurrently Write and Read from a File.
type Stream struct {
	mu        sync.Mutex
	b         *broadcaster
	file      File
	fs        FileSystem
	closeOnce onceWithErr
}

// New creates a new Stream from the StdFileSystem with Name "name".
func New(name string) (*Stream, error) {
	return NewStream(name, StdFileSystem)
}

// NewStream creates a new Stream with Name "name" in FileSystem fs.
func NewStream(name string, fs FileSystem) (*Stream, error) {
	f, err := fs.Create(name)
	return newStream(f, fs), err
}

// NewMemStream creates an in-memory stream with no name, and no underlying fs.
// This should replace uses of NewStream("name", NewMemFs()).
// Remove() is unsupported as there is no fs to remove it from.
func NewMemStream() *Stream {
	f := newMemFile("")
	return newStream(f, singletonFs{f})
}

func newStream(file File, fs FileSystem) *Stream {
	return &Stream{
		file: file,
		fs:   fs,
		b:    newBroadcaster(),
	}
}

type singletonFs struct {
	file *memFile
}

func (fs singletonFs) Create(key string) (File, error) { return nil, ErrUnsupported }

func (fs singletonFs) Open(key string) (File, error) { return &memReader{memFile: fs.file}, nil }

func (fs singletonFs) Remove(key string) error { return ErrUnsupported }

// Name returns the name of the underlying File in the FileSystem.
func (s *Stream) Name() string { return s.file.Name() }

// Write writes p to the Stream. It's concurrent safe to be called with Stream's other methods.
func (s *Stream) Write(p []byte) (int, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	n, err := s.file.Write(p)
	s.b.Wrote(n)
	return n, err
}

// Close will close the active stream. This will cause Readers to return EOF once they have
// read the entire stream.
func (s *Stream) Close() error {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.closeOnce.Do(func() (err error) {
		err = s.file.Close()
		s.b.Close()
		return err
	})
}

// Remove will block until the Stream and all its Readers have been Closed,
// at which point it will delete the underlying file. NextReader() will return
// ErrRemoving if called after Remove.
func (s *Stream) Remove() error {
	s.shutdownWithErr(ErrRemoving)
	return s.fs.Remove(s.file.Name())
}

// shutdownWithErr causes NextReader to stop creating new Readers and instead return err, this
// method also blocks until all Readers and the Writer have closed.
func (s *Stream) shutdownWithErr(err error) {
	if err == nil {
		return
	}
	s.b.PreventNewHandles(err) // no new readers can be created, but existing ones can finish, same with the writer
	s.b.WaitForZeroHandles()   // wait for exiting handles to finish up
}

// Cancel signals that this Stream is forcibly ending, NextReader() will fail, existing readers will fail Reads, all Readers & Writer are Closed.
// This call is non-blocking, and Remove() after this call is non-blocking.
func (s *Stream) Cancel() error {
	s.b.Cancel()     // all existing reads are canceled, no new reads will occur, all readers closed
	return s.Close() // all writes are stopped
}

// Shutdown signals that this Stream is not accepting more readers and wait for existing ones to close
// NextReader() will fail, existing readers will fail Reads, all Readers & Writer are Closed.
// This call is non-blocking, and Remove() after this call is non-blocking.
func (s *Stream) Shutdown() error {
	err := s.Close()
	if err != nil {
		return err
	}
	s.b.Shutdown() // all existing reads are canceled, no new reads will occur, block until all reads are closed
	return nil
}

// NextReader will return a concurrent-safe Reader for this stream. Each Reader will
// see a complete and independent view of the stream, and can Read while the stream
// is written to.
func (s *Stream) NextReader() (*Reader, error) {
	return s.b.NewReader(func() (*Reader, error) {
		file, err := s.fs.Open(s.file.Name())
		if err != nil {
			return nil, err
		}
		return &Reader{file: file, s: s}, nil
	})
}
<|endoftext|>


<commit_before>package sctp

import (
	"math"
	"sync"

	"github.com/pions/logging"
	"github.com/pkg/errors"
)

const (
	// ReliabilityTypeReliable is used for reliable transmission
	ReliabilityTypeReliable byte = 0
	// ReliabilityTypeRexmit is used for partial reliability by retransmission count
	ReliabilityTypeRexmit byte = 1
	// ReliabilityTypeTimed is used for partial reliability by retransmission duration
	ReliabilityTypeTimed byte = 2
)

// Stream represents an SCTP stream
type Stream struct {
	association         *Association
	lock                sync.RWMutex
	streamIdentifier    uint16
	defaultPayloadType  PayloadProtocolIdentifier
	reassemblyQueue     *reassemblyQueue
	sequenceNumber      uint16
	readNotifier        *sync.Cond
	readErr             error
	writeErr            error
	unordered           bool
	reliabilityType     byte
	reliabilityValue    uint32
	bufferedAmount      uint64
	bufferedAmountLow   uint64
	onBufferedAmountLow func()
	log                 *logging.LeveledLogger
}

// StreamIdentifier returns the Stream identifier associated to the stream.
func (s *Stream) StreamIdentifier() uint16 {
	s.lock.RLock()
	defer s.lock.RUnlock()
	return s.streamIdentifier
}

// SetDefaultPayloadType sets the default payload type used by Write.
func (s *Stream) SetDefaultPayloadType(defaultPayloadType PayloadProtocolIdentifier) {
	s.lock.Lock()
	defer s.lock.Unlock()

	s.setDefaultPayloadType(defaultPayloadType)
}

// setDefaultPayloadType sets the defaultPayloadType. The caller should hold the lock.
func (s *Stream) setDefaultPayloadType(defaultPayloadType PayloadProtocolIdentifier) {
	s.defaultPayloadType = defaultPayloadType
}

// SetReliabilityParams sets reliability parameters for this stream.
func (s *Stream) SetReliabilityParams(unordered bool, relType byte, relVal uint32) {
	s.lock.Lock()
	defer s.lock.Unlock()

	s.setReliabilityParams(unordered, relType, relVal)
}

// setReliabilityParams sets reliability parameters for this stream.
// The caller should hold the lock.
func (s *Stream) setReliabilityParams(unordered bool, relType byte, relVal uint32) {
	s.unordered = unordered
	s.reliabilityType = relType
	s.reliabilityValue = relVal
}

// Read reads a packet of len(p) bytes, dropping the Payload Protocol Identifier.
// Returns EOF when the stream is reset or an error if the stream is closed
// otherwise.
func (s *Stream) Read(p []byte) (int, error) {
	n, _, err := s.ReadSCTP(p)
	return n, err
}

// ReadSCTP reads a packet of len(p) bytes and returns the associated Payload Protocol Identifier.
// Returns EOF when the stream is reset or an error if the stream is closed
// otherwise.
func (s *Stream) ReadSCTP(p []byte) (int, PayloadProtocolIdentifier, error) {
	for {
		s.lock.Lock()
		n, ppi, err := s.reassemblyQueue.read(p)
		s.lock.Unlock()
		if err == nil {
			return n, ppi, nil
		}

		s.lock.RLock()
		err = s.readErr
		if err != nil {
			s.lock.RUnlock()
			return 0, PayloadProtocolIdentifier(0), err
		}

		notifier := s.readNotifier
		s.lock.RUnlock()
		// Wait for read notification
		if notifier != nil {
			notifier.L.Lock()
			notifier.Wait()
			notifier.L.Unlock()
		}
	}
}

func (s *Stream) handleData(pd *chunkPayloadData) {
	var readable bool
	s.lock.Lock()
	//fmt.Printf("stream[%d]: handleData ts=%d ssn=%d\n", s.streamIdentifier, pd.tsn, pd.streamSequenceNumber)
	if s.reassemblyQueue.push(pd) {
		readable = s.reassemblyQueue.isReadable()
	}
	//fmt.Printf("stream[%d]: handleData readable? %v\n", s.streamIdentifier, readable)
	s.lock.Unlock()

	// Notify the reader asynchronously if there's a data chunk to read.
	if readable {
		s.readNotifier.Signal()
	}
}

func (s *Stream) handleForwardTSN(newCumulativeTSN uint32, ssn uint16) {
	var readable bool
	s.lock.Lock()
	// Remove all chunks older than or equal to the new TSN from
	// the reassemblyQueue.
	//fmt.Printf("stream[%d]: handleForwardTSN newTSN=%d ssn=%d\n", s.streamIdentifier, newCumulativeTSN, ssn)
	s.reassemblyQueue.forwardTSN(newCumulativeTSN, s.unordered, ssn)
	readable = s.reassemblyQueue.isReadable()
	//fmt.Printf("stream[%d]: handleForwardTSN readable? %v\n", s.streamIdentifier, readable)
	s.lock.Unlock()

	// Notify the reader asynchronously if there's a data chunk to read.
	if readable {
		s.readNotifier.Signal()
	}
}

// Write writes len(p) bytes from p with the default Payload Protocol Identifier
func (s *Stream) Write(p []byte) (n int, err error) {
	return s.WriteSCTP(p, s.defaultPayloadType)
}

// WriteSCTP writes len(p) bytes from p to the DTLS connection
func (s *Stream) WriteSCTP(p []byte, ppi PayloadProtocolIdentifier) (n int, err error) {
	if len(p) > math.MaxUint16 {
		return 0, errors.Errorf("Outbound packet larger than maximum message size %v", math.MaxUint16)
	}

	s.lock.RLock()
	err = s.writeErr
	s.lock.RUnlock()
	if err != nil {
		return 0, err
	}

	chunks := s.packetize(p, ppi)

	return len(p), s.association.sendPayloadData(chunks)
}

func (s *Stream) packetize(raw []byte, ppi PayloadProtocolIdentifier) []*chunkPayloadData {
	s.lock.Lock()
	defer s.lock.Unlock()

	i := uint32(0)
	remaining := uint32(len(raw))

	// From draft-ietf-rtcweb-data-protocol-09, section 6:
	//   All Data Channel Establishment Protocol messages MUST be sent using
	//   ordered delivery and reliable transmission.
	unordered := ppi != PayloadTypeWebRTCDCEP && s.unordered

	var chunks []*chunkPayloadData
	for remaining != 0 {
		fragmentSize := min32(s.association.maxPayloadSize, remaining)

		// Copy the userdata since we'll have to store it until acked
		// and the caller may re-use the buffer in the mean time
		userData := make([]byte, fragmentSize)
		copy(userData, raw[i:i+fragmentSize])

		chunk := &chunkPayloadData{
			streamIdentifier:     s.streamIdentifier,
			userData:             userData,
			unordered:            unordered,
			beginningFragment:    i == 0,
			endingFragment:       remaining-fragmentSize == 0,
			immediateSack:        false,
			payloadType:          ppi,
			streamSequenceNumber: s.sequenceNumber,
		}

		chunks = append(chunks, chunk)

		remaining -= fragmentSize
		i += fragmentSize
	}

	// RFC 4960 Sec 6.6
	// Note: When transmitting ordered and unordered data, an endpoint does
	// not increment its Stream Sequence Number when transmitting a DATA
	// chunk with U flag set to 1.
	if !unordered {
		s.sequenceNumber++
	}

	s.bufferedAmount += uint64(len(raw))
	s.log.Tracef("sctp/stream: bufferedAmount = %d", s.bufferedAmount)

	return chunks
}

// Close closes the write-direction of the stream.
// Future calls to Write are not permitted after calling Close.
func (s *Stream) Close() error {
	s.lock.Lock()
	if s.writeErr != nil {
		s.lock.Unlock()
		return nil // already closed
	}
	s.writeErr = errors.New("Stream closed")

	a := s.association
	sid := s.streamIdentifier
	s.lock.Unlock()

	// Reset the outgoing stream
	// https://tools.ietf.org/html/rfc6525
	return a.sendResetRequest(sid)
}

// BufferedAmount returns the number of bytes of data currently queued to be sent over this stream.
func (s *Stream) BufferedAmount() uint64 {
	s.lock.RLock()
	defer s.lock.RUnlock()

	return s.bufferedAmount
}

// BufferedAmountLowThreshold returns the number of bytes of buffered outgoing data that is
// considered "low." Defaults to 0.
func (s *Stream) BufferedAmountLowThreshold() uint64 {
	s.lock.RLock()
	defer s.lock.RUnlock()

	return s.bufferedAmountLow
}

// SetBufferedAmountLowThreshold is used to update the threshold.
// See BufferedAmountLowThreshold().
func (s *Stream) SetBufferedAmountLowThreshold(th uint64) {
	s.lock.Lock()
	defer s.lock.Unlock()

	s.bufferedAmountLow = th
}

// OnBufferedAmountLow sets the callback handler which would be called when the number of
// bytes of outgoing data buffered is lower than the threshold.
func (s *Stream) OnBufferedAmountLow(f func()) {
	s.lock.Lock()
	defer s.lock.Unlock()

	s.onBufferedAmountLow = f
}

// This method is called by association's readLoop (go-)routine to notify this stream
// of the specified amount of outgoing data has been delivered to the peer.
func (s *Stream) onBufferReleased(nBytesReleased int) {
	s.lock.Lock()
	defer s.lock.Unlock()

	if s.bufferedAmount < uint64(nBytesReleased) {
		s.bufferedAmount = 0
		s.log.Errorf("released buffer size %d should be <= %d", nBytesReleased, s.bufferedAmount)
	} else {
		s.bufferedAmount -= uint64(nBytesReleased)
	}

	s.log.Tracef("sctp/stream: bufferedAmount = %d", s.bufferedAmount)

	if s.onBufferedAmountLow != nil && s.bufferedAmount < s.bufferedAmountLow {
		s.lock.Unlock()
		s.onBufferedAmountLow()
		s.lock.Lock()
	}
}
<commit_msg>Fix potential race on the callback<commit_after>package sctp

import (
	"math"
	"sync"

	"github.com/pions/logging"
	"github.com/pkg/errors"
)

const (
	// ReliabilityTypeReliable is used for reliable transmission
	ReliabilityTypeReliable byte = 0
	// ReliabilityTypeRexmit is used for partial reliability by retransmission count
	ReliabilityTypeRexmit byte = 1
	// ReliabilityTypeTimed is used for partial reliability by retransmission duration
	ReliabilityTypeTimed byte = 2
)

// Stream represents an SCTP stream
type Stream struct {
	association         *Association
	lock                sync.RWMutex
	streamIdentifier    uint16
	defaultPayloadType  PayloadProtocolIdentifier
	reassemblyQueue     *reassemblyQueue
	sequenceNumber      uint16
	readNotifier        *sync.Cond
	readErr             error
	writeErr            error
	unordered           bool
	reliabilityType     byte
	reliabilityValue    uint32
	bufferedAmount      uint64
	bufferedAmountLow   uint64
	onBufferedAmountLow func()
	log                 *logging.LeveledLogger
}

// StreamIdentifier returns the Stream identifier associated to the stream.
func (s *Stream) StreamIdentifier() uint16 {
	s.lock.RLock()
	defer s.lock.RUnlock()
	return s.streamIdentifier
}

// SetDefaultPayloadType sets the default payload type used by Write.
func (s *Stream) SetDefaultPayloadType(defaultPayloadType PayloadProtocolIdentifier) {
	s.lock.Lock()
	defer s.lock.Unlock()

	s.setDefaultPayloadType(defaultPayloadType)
}

// setDefaultPayloadType sets the defaultPayloadType. The caller should hold the lock.
func (s *Stream) setDefaultPayloadType(defaultPayloadType PayloadProtocolIdentifier) {
	s.defaultPayloadType = defaultPayloadType
}

// SetReliabilityParams sets reliability parameters for this stream.
func (s *Stream) SetReliabilityParams(unordered bool, relType byte, relVal uint32) {
	s.lock.Lock()
	defer s.lock.Unlock()

	s.setReliabilityParams(unordered, relType, relVal)
}

// setReliabilityParams sets reliability parameters for this stream.
// The caller should hold the lock.
func (s *Stream) setReliabilityParams(unordered bool, relType byte, relVal uint32) {
	s.unordered = unordered
	s.reliabilityType = relType
	s.reliabilityValue = relVal
}

// Read reads a packet of len(p) bytes, dropping the Payload Protocol Identifier.
// Returns EOF when the stream is reset or an error if the stream is closed
// otherwise.
func (s *Stream) Read(p []byte) (int, error) {
	n, _, err := s.ReadSCTP(p)
	return n, err
}

// ReadSCTP reads a packet of len(p) bytes and returns the associated Payload Protocol Identifier.
// Returns EOF when the stream is reset or an error if the stream is closed
// otherwise.
func (s *Stream) ReadSCTP(p []byte) (int, PayloadProtocolIdentifier, error) {
	for {
		s.lock.Lock()
		n, ppi, err := s.reassemblyQueue.read(p)
		s.lock.Unlock()
		if err == nil {
			return n, ppi, nil
		}

		s.lock.RLock()
		err = s.readErr
		if err != nil {
			s.lock.RUnlock()
			return 0, PayloadProtocolIdentifier(0), err
		}

		notifier := s.readNotifier
		s.lock.RUnlock()
		// Wait for read notification
		if notifier != nil {
			notifier.L.Lock()
			notifier.Wait()
			notifier.L.Unlock()
		}
	}
}

func (s *Stream) handleData(pd *chunkPayloadData) {
	var readable bool
	s.lock.Lock()
	//fmt.Printf("stream[%d]: handleData ts=%d ssn=%d\n", s.streamIdentifier, pd.tsn, pd.streamSequenceNumber)
	if s.reassemblyQueue.push(pd) {
		readable = s.reassemblyQueue.isReadable()
	}
	//fmt.Printf("stream[%d]: handleData readable? %v\n", s.streamIdentifier, readable)
	s.lock.Unlock()

	// Notify the reader asynchronously if there's a data chunk to read.
	if readable {
		s.readNotifier.Signal()
	}
}

func (s *Stream) handleForwardTSN(newCumulativeTSN uint32, ssn uint16) {
	var readable bool
	s.lock.Lock()
	// Remove all chunks older than or equal to the new TSN from
	// the reassemblyQueue.
	//fmt.Printf("stream[%d]: handleForwardTSN newTSN=%d ssn=%d\n", s.streamIdentifier, newCumulativeTSN, ssn)
	s.reassemblyQueue.forwardTSN(newCumulativeTSN, s.unordered, ssn)
	readable = s.reassemblyQueue.isReadable()
	//fmt.Printf("stream[%d]: handleForwardTSN readable? %v\n", s.streamIdentifier, readable)
	s.lock.Unlock()

	// Notify the reader asynchronously if there's a data chunk to read.
	if readable {
		s.readNotifier.Signal()
	}
}

// Write writes len(p) bytes from p with the default Payload Protocol Identifier
func (s *Stream) Write(p []byte) (n int, err error) {
	return s.WriteSCTP(p, s.defaultPayloadType)
}

// WriteSCTP writes len(p) bytes from p to the DTLS connection
func (s *Stream) WriteSCTP(p []byte, ppi PayloadProtocolIdentifier) (n int, err error) {
	if len(p) > math.MaxUint16 {
		return 0, errors.Errorf("Outbound packet larger than maximum message size %v", math.MaxUint16)
	}

	s.lock.RLock()
	err = s.writeErr
	s.lock.RUnlock()
	if err != nil {
		return 0, err
	}

	chunks := s.packetize(p, ppi)

	return len(p), s.association.sendPayloadData(chunks)
}

func (s *Stream) packetize(raw []byte, ppi PayloadProtocolIdentifier) []*chunkPayloadData {
	s.lock.Lock()
	defer s.lock.Unlock()

	i := uint32(0)
	remaining := uint32(len(raw))

	// From draft-ietf-rtcweb-data-protocol-09, section 6:
	//   All Data Channel Establishment Protocol messages MUST be sent using
	//   ordered delivery and reliable transmission.
	unordered := ppi != PayloadTypeWebRTCDCEP && s.unordered

	var chunks []*chunkPayloadData
	for remaining != 0 {
		fragmentSize := min32(s.association.maxPayloadSize, remaining)

		// Copy the userdata since we'll have to store it until acked
		// and the caller may re-use the buffer in the mean time
		userData := make([]byte, fragmentSize)
		copy(userData, raw[i:i+fragmentSize])

		chunk := &chunkPayloadData{
			streamIdentifier:     s.streamIdentifier,
			userData:             userData,
			unordered:            unordered,
			beginningFragment:    i == 0,
			endingFragment:       remaining-fragmentSize == 0,
			immediateSack:        false,
			payloadType:          ppi,
			streamSequenceNumber: s.sequenceNumber,
		}

		chunks = append(chunks, chunk)

		remaining -= fragmentSize
		i += fragmentSize
	}

	// RFC 4960 Sec 6.6
	// Note: When transmitting ordered and unordered data, an endpoint does
	// not increment its Stream Sequence Number when transmitting a DATA
	// chunk with U flag set to 1.
	if !unordered {
		s.sequenceNumber++
	}

	s.bufferedAmount += uint64(len(raw))
	s.log.Tracef("sctp/stream: bufferedAmount = %d", s.bufferedAmount)

	return chunks
}

// Close closes the write-direction of the stream.
// Future calls to Write are not permitted after calling Close.
func (s *Stream) Close() error {
	s.lock.Lock()
	if s.writeErr != nil {
		s.lock.Unlock()
		return nil // already closed
	}
	s.writeErr = errors.New("Stream closed")

	a := s.association
	sid := s.streamIdentifier
	s.lock.Unlock()

	// Reset the outgoing stream
	// https://tools.ietf.org/html/rfc6525
	return a.sendResetRequest(sid)
}

// BufferedAmount returns the number of bytes of data currently queued to be sent over this stream.
func (s *Stream) BufferedAmount() uint64 {
	s.lock.RLock()
	defer s.lock.RUnlock()

	return s.bufferedAmount
}

// BufferedAmountLowThreshold returns the number of bytes of buffered outgoing data that is
// considered "low." Defaults to 0.
func (s *Stream) BufferedAmountLowThreshold() uint64 {
	s.lock.RLock()
	defer s.lock.RUnlock()

	return s.bufferedAmountLow
}

// SetBufferedAmountLowThreshold is used to update the threshold.
// See BufferedAmountLowThreshold().
func (s *Stream) SetBufferedAmountLowThreshold(th uint64) {
	s.lock.Lock()
	defer s.lock.Unlock()

	s.bufferedAmountLow = th
}

// OnBufferedAmountLow sets the callback handler which would be called when the number of
// bytes of outgoing data buffered is lower than the threshold.
func (s *Stream) OnBufferedAmountLow(f func()) {
	s.lock.Lock()
	defer s.lock.Unlock()

	s.onBufferedAmountLow = f
}

// This method is called by association's readLoop (go-)routine to notify this stream
// of the specified amount of outgoing data has been delivered to the peer.
func (s *Stream) onBufferReleased(nBytesReleased int) {
	s.lock.Lock()
	defer s.lock.Unlock()

	if s.bufferedAmount < uint64(nBytesReleased) {
		s.bufferedAmount = 0
		s.log.Errorf("released buffer size %d should be <= %d", nBytesReleased, s.bufferedAmount)
	} else {
		s.bufferedAmount -= uint64(nBytesReleased)
	}

	s.log.Tracef("sctp/stream: bufferedAmount = %d", s.bufferedAmount)

	if s.onBufferedAmountLow != nil && s.bufferedAmount < s.bufferedAmountLow {
		f := s.onBufferedAmountLow
		s.lock.Unlock()
		f()
		s.lock.Lock()
	}
}
<|endoftext|>


<commit_before>// Package stripe provides the binding for Stripe REST APIs.
package stripe

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"time"
)

const (
	apiURL     = "https://api.stripe.com/v1"
	uploadsURL = "https://uploads.stripe.com/v1"
)

// apiversion is the currently supported API version
const apiversion = "2017-04-06"

// clientversion is the binding version
const clientversion = "21.5.1"

// defaultHTTPTimeout is the default timeout on the http.Client used by the library.
// This is chosen to be consistent with the other Stripe language libraries and
// to coordinate with other timeouts configured in the Stripe infrastructure.
const defaultHTTPTimeout = 80 * time.Second

// TotalBackends is the total number of Stripe API endpoints supported by the
// binding.
const TotalBackends = 2

// UnknownPlatform is the string returned as the system name if we couldn't get
// one from `uname`.
const UnknownPlatform = "unknown platform"

// AppInfo contains information about the "app" which this integration belongs
// to. This should be reserved for plugins that wish to identify themselves
// with Stripe.
type AppInfo struct {
	Name    string `json:"name"`
	URL     string `json:"url"`
	Version string `json:"version"`
}

// formatUserAgent formats an AppInfo in a way that's suitable to be appended
// to a User-Agent string. Note that this format is shared between all
// libraries so if it's changed, it should be changed everywhere.
func (a *AppInfo) formatUserAgent() string {
	str := a.Name
	if a.Version != "" {
		str += "/" + a.Version
	}
	if a.URL != "" {
		str += " (" + a.URL + ")"
	}
	return str
}

// Backend is an interface for making calls against a Stripe service.
// This interface exists to enable mocking for during testing if needed.
type Backend interface {
	Call(method, path, key string, body *RequestValues, params *Params, v interface{}) error
	CallMultipart(method, path, key, boundary string, body io.Reader, params *Params, v interface{}) error
}

// BackendConfiguration is the internal implementation for making HTTP calls to Stripe.
type BackendConfiguration struct {
	Type       SupportedBackend
	URL        string
	HTTPClient *http.Client
}

// SupportedBackend is an enumeration of supported Stripe endpoints.
// Currently supported values are "api" and "uploads".
type SupportedBackend string

const (
	// APIBackend is a constant representing the API service backend.
	APIBackend SupportedBackend = "api"

	// APIURL is the URL of the API service backend.
	APIURL string = "https://api.stripe.com/v1"

	// UploadsBackend is a constant representing the uploads service backend.
	UploadsBackend SupportedBackend = "uploads"

	// UploadsURL is the URL of the uploads service backend.
	UploadsURL string = "https://uploads.stripe.com/v1"
)

// Backends are the currently supported endpoints.
type Backends struct {
	API, Uploads Backend
}

// stripeClientUserAgent contains information about the current runtime which
// is serialized and sent in the `X-Stripe-Client-User-Agent` as additional
// debugging information.
type stripeClientUserAgent struct {
	Application     *AppInfo `json:"application"`
	BindingsVersion string   `json:"bindings_version"`
	Language        string   `json:"language"`
	LanguageVersion string   `json:"language_version"`
	Publisher       string   `json:"publisher"`
	Uname           string   `json:"uname"`
}

// Key is the Stripe API key used globally in the binding.
var Key string

// LogLevel is the logging level for this library.
// 0: no logging
// 1: errors only
// 2: errors + informational (default)
// 3: errors + informational + debug
var LogLevel = 2

// Logger controls how stripe performs logging at a package level. It is useful
// to customise if you need it prefixed for your application to meet other
// requirements
var Logger *log.Logger

func init() {
	Logger = log.New(os.Stderr, "", log.LstdFlags)
	initUserAgent()
}

var appInfo *AppInfo
var httpClient = &http.Client{Timeout: defaultHTTPTimeout}
var backends Backends
var encodedStripeUserAgent string
var encodedUserAgent string

// SetHTTPClient overrides the default HTTP client.
// This is useful if you're running in a Google AppEngine environment
// where the http.DefaultClient is not available.
func SetHTTPClient(client *http.Client) {
	httpClient = client
}

// NewBackends creates a new set of backends with the given HTTP client. You
// should only need to use this for testing purposes or on App Engine.
func NewBackends(httpClient *http.Client) *Backends {
	return &Backends{
		API: BackendConfiguration{
			APIBackend, APIURL, httpClient},
		Uploads: BackendConfiguration{
			UploadsBackend, UploadsURL, httpClient},
	}
}

// GetBackend returns the currently used backend in the binding.
func GetBackend(backend SupportedBackend) Backend {
	var ret Backend
	switch backend {
	case APIBackend:
		if backends.API == nil {
			backends.API = BackendConfiguration{backend, apiURL, httpClient}
		}

		ret = backends.API
	case UploadsBackend:
		if backends.Uploads == nil {
			backends.Uploads = BackendConfiguration{backend, uploadsURL, httpClient}
		}
		ret = backends.Uploads
	}

	return ret
}

// SetBackend sets the backend used in the binding.
func SetBackend(backend SupportedBackend, b Backend) {
	switch backend {
	case APIBackend:
		backends.API = b
	case UploadsBackend:
		backends.Uploads = b
	}
}

// Call is the Backend.Call implementation for invoking Stripe APIs.
func (s BackendConfiguration) Call(method, path, key string, form *RequestValues, params *Params, v interface{}) error {
	var body io.Reader
	if form != nil && !form.Empty() {
		data := form.Encode()
		if strings.ToUpper(method) == "GET" {
			path += "?" + data
		} else {
			body = bytes.NewBufferString(data)
		}
	}

	req, err := s.NewRequest(method, path, key, "application/x-www-form-urlencoded", body, params)
	if err != nil {
		return err
	}

	if err := s.Do(req, v); err != nil {
		return err
	}

	return nil
}

// CallMultipart is the Backend.CallMultipart implementation for invoking Stripe APIs.
func (s BackendConfiguration) CallMultipart(method, path, key, boundary string, body io.Reader, params *Params, v interface{}) error {
	contentType := "multipart/form-data; boundary=" + boundary

	req, err := s.NewRequest(method, path, key, contentType, body, params)
	if err != nil {
		return err
	}

	if err := s.Do(req, v); err != nil {
		return err
	}

	return nil
}

// NewRequest is used by Call to generate an http.Request. It handles encoding
// parameters and attaching the appropriate headers.
func (s *BackendConfiguration) NewRequest(method, path, key, contentType string, body io.Reader, params *Params) (*http.Request, error) {
	if !strings.HasPrefix(path, "/") {
		path = "/" + path
	}

	path = s.URL + path

	req, err := http.NewRequest(method, path, body)
	if err != nil {
		if LogLevel > 0 {
			Logger.Printf("Cannot create Stripe request: %v\n", err)
		}
		return nil, err
	}

	req.SetBasicAuth(key, "")

	if params != nil {
		if idempotency := strings.TrimSpace(params.IdempotencyKey); idempotency != "" {
			if len(idempotency) > 255 {
				return nil, errors.New("Cannot use an IdempotencyKey longer than 255 characters long.")
			}

			req.Header.Add("Idempotency-Key", idempotency)
		}

		// Support the value of the old Account field for now.
		if account := strings.TrimSpace(params.Account); account != "" {
			req.Header.Add("Stripe-Account", account)
		}

		// But prefer StripeAccount.
		if stripeAccount := strings.TrimSpace(params.StripeAccount); stripeAccount != "" {
			req.Header.Add("Stripe-Account", stripeAccount)
		}
	}

	req.Header.Add("Stripe-Version", apiversion)
	req.Header.Add("User-Agent", encodedUserAgent)
	req.Header.Add("Content-Type", contentType)
	req.Header.Add("X-Stripe-Client-User-Agent", encodedStripeUserAgent)

	return req, nil
}

// Do is used by Call to execute an API request and parse the response. It uses
// the backend's HTTP client to execute the request and unmarshals the response
// into v. It also handles unmarshaling errors returned by the API.
func (s *BackendConfiguration) Do(req *http.Request, v interface{}) error {
	if LogLevel > 1 {
		Logger.Printf("Requesting %v %v%v\n", req.Method, req.URL.Host, req.URL.Path)
	}

	start := time.Now()

	res, err := s.HTTPClient.Do(req)

	if LogLevel > 2 {
		Logger.Printf("Completed in %v\n", time.Since(start))
	}

	if err != nil {
		if LogLevel > 0 {
			Logger.Printf("Request to Stripe failed: %v\n", err)
		}
		return err
	}
	defer res.Body.Close()

	resBody, err := ioutil.ReadAll(res.Body)
	if err != nil {
		if LogLevel > 0 {
			Logger.Printf("Cannot parse Stripe response: %v\n", err)
		}
		return err
	}

	if res.StatusCode >= 400 {
		return s.ResponseToError(res, resBody)
	}

	if LogLevel > 2 {
		Logger.Printf("Stripe Response: %q\n", resBody)
	}

	if v != nil {
		return json.Unmarshal(resBody, v)
	}

	return nil
}

func (s *BackendConfiguration) ResponseToError(res *http.Response, resBody []byte) error {
	// for some odd reason, the Erro structure doesn't unmarshal
	// initially I thought it was because it's a struct inside of a struct
	// but even after trying that, it still didn't work
	// so unmarshalling to a map for now and parsing the results manually
	// but should investigate later
	var errMap map[string]interface{}
	json.Unmarshal(resBody, &errMap)

	e, ok := errMap["error"]
	if !ok {
		err := errors.New(string(resBody))
		if LogLevel > 0 {
			Logger.Printf("Unparsable error returned from Stripe: %v\n", err)
		}
		return err
	}

	root := e.(map[string]interface{})

	stripeErr := &Error{
		Type:           ErrorType(root["type"].(string)),
		Msg:            root["message"].(string),
		HTTPStatusCode: res.StatusCode,
		RequestID:      res.Header.Get("Request-Id"),
	}

	if code, ok := root["code"]; ok {
		stripeErr.Code = ErrorCode(code.(string))
	}

	if param, ok := root["param"]; ok {
		stripeErr.Param = param.(string)
	}

	if charge, ok := root["charge"]; ok {
		stripeErr.ChargeID = charge.(string)
	}

	switch stripeErr.Type {
	case ErrorTypeAPI:
		stripeErr.Err = &APIError{stripeErr: stripeErr}

	case ErrorTypeAPIConnection:
		stripeErr.Err = &APIConnectionError{stripeErr: stripeErr}

	case ErrorTypeAuthentication:
		stripeErr.Err = &AuthenticationError{stripeErr: stripeErr}

	case ErrorTypeCard:
		cardErr := &CardError{stripeErr: stripeErr}
		stripeErr.Err = cardErr

		if declineCode, ok := root["decline_code"]; ok {
			cardErr.DeclineCode = declineCode.(string)
		}

	case ErrorTypeInvalidRequest:
		stripeErr.Err = &InvalidRequestError{stripeErr: stripeErr}

	case ErrorTypePermission:
		stripeErr.Err = &PermissionError{stripeErr: stripeErr}

	case ErrorTypeRateLimit:
		stripeErr.Err = &RateLimitError{stripeErr: stripeErr}
	}

	if LogLevel > 0 {
		Logger.Printf("Error encountered from Stripe: %v\n", stripeErr)
	}

	return stripeErr
}

// SetAppInfo sets app information. See AppInfo.
func SetAppInfo(info *AppInfo) {
	if info != nil && info.Name == "" {
		panic(fmt.Errorf("App info name cannot be empty"))
	}
	appInfo = info

	// This is run in init, but we need to reinitialize it now that we have
	// some app info.
	initUserAgent()
}

// getUname tries to get a uname from the system, but not that hard. It tries
// to execute `uname -a`, but swallows any errors in case that didn't work
// (i.e. non-Unix non-Mac system or some other reason).
func getUname() string {
	path, err := exec.LookPath("uname")
	if err != nil {
		return UnknownPlatform
	}

	cmd := exec.Command(path, "-a")
	var out bytes.Buffer
	cmd.Stderr = nil // goes to os.DevNull
	cmd.Stdout = &out
	err = cmd.Run()
	if err != nil {
		return UnknownPlatform
	}

	return out.String()
}

func initUserAgent() {
	encodedUserAgent = "Stripe/v1 GoBindings/" + clientversion
	if appInfo != nil {
		encodedUserAgent += " " + appInfo.formatUserAgent()
	}

	stripeUserAgent := &stripeClientUserAgent{
		Application:     appInfo,
		BindingsVersion: clientversion,
		Language:        "go",
		LanguageVersion: runtime.Version(),
		Publisher:       "stripe",
		Uname:           getUname(),
	}
	marshaled, err := json.Marshal(stripeUserAgent)
	// Encoding this struct should never be a problem, so we're okay to panic
	// in case it is for some reason.
	if err != nil {
		panic(err)
	}
	encodedStripeUserAgent = string(marshaled)
}
<commit_msg>Bump the supported API version to 2017-05-25<commit_after>// Package stripe provides the binding for Stripe REST APIs.
package stripe

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"time"
)

const (
	apiURL     = "https://api.stripe.com/v1"
	uploadsURL = "https://uploads.stripe.com/v1"
)

// apiversion is the currently supported API version
const apiversion = "2017-05-25"

// clientversion is the binding version
const clientversion = "21.5.1"

// defaultHTTPTimeout is the default timeout on the http.Client used by the library.
// This is chosen to be consistent with the other Stripe language libraries and
// to coordinate with other timeouts configured in the Stripe infrastructure.
const defaultHTTPTimeout = 80 * time.Second

// TotalBackends is the total number of Stripe API endpoints supported by the
// binding.
const TotalBackends = 2

// UnknownPlatform is the string returned as the system name if we couldn't get
// one from `uname`.
const UnknownPlatform = "unknown platform"

// AppInfo contains information about the "app" which this integration belongs
// to. This should be reserved for plugins that wish to identify themselves
// with Stripe.
type AppInfo struct {
	Name    string `json:"name"`
	URL     string `json:"url"`
	Version string `json:"version"`
}

// formatUserAgent formats an AppInfo in a way that's suitable to be appended
// to a User-Agent string. Note that this format is shared between all
// libraries so if it's changed, it should be changed everywhere.
func (a *AppInfo) formatUserAgent() string {
	str := a.Name
	if a.Version != "" {
		str += "/" + a.Version
	}
	if a.URL != "" {
		str += " (" + a.URL + ")"
	}
	return str
}

// Backend is an interface for making calls against a Stripe service.
// This interface exists to enable mocking for during testing if needed.
type Backend interface {
	Call(method, path, key string, body *RequestValues, params *Params, v interface{}) error
	CallMultipart(method, path, key, boundary string, body io.Reader, params *Params, v interface{}) error
}

// BackendConfiguration is the internal implementation for making HTTP calls to Stripe.
type BackendConfiguration struct {
	Type       SupportedBackend
	URL        string
	HTTPClient *http.Client
}

// SupportedBackend is an enumeration of supported Stripe endpoints.
// Currently supported values are "api" and "uploads".
type SupportedBackend string

const (
	// APIBackend is a constant representing the API service backend.
	APIBackend SupportedBackend = "api"

	// APIURL is the URL of the API service backend.
	APIURL string = "https://api.stripe.com/v1"

	// UploadsBackend is a constant representing the uploads service backend.
	UploadsBackend SupportedBackend = "uploads"

	// UploadsURL is the URL of the uploads service backend.
	UploadsURL string = "https://uploads.stripe.com/v1"
)

// Backends are the currently supported endpoints.
type Backends struct {
	API, Uploads Backend
}

// stripeClientUserAgent contains information about the current runtime which
// is serialized and sent in the `X-Stripe-Client-User-Agent` as additional
// debugging information.
type stripeClientUserAgent struct {
	Application     *AppInfo `json:"application"`
	BindingsVersion string   `json:"bindings_version"`
	Language        string   `json:"language"`
	LanguageVersion string   `json:"language_version"`
	Publisher       string   `json:"publisher"`
	Uname           string   `json:"uname"`
}

// Key is the Stripe API key used globally in the binding.
var Key string

// LogLevel is the logging level for this library.
// 0: no logging
// 1: errors only
// 2: errors + informational (default)
// 3: errors + informational + debug
var LogLevel = 2

// Logger controls how stripe performs logging at a package level. It is useful
// to customise if you need it prefixed for your application to meet other
// requirements
var Logger *log.Logger

func init() {
	Logger = log.New(os.Stderr, "", log.LstdFlags)
	initUserAgent()
}

var appInfo *AppInfo
var httpClient = &http.Client{Timeout: defaultHTTPTimeout}
var backends Backends
var encodedStripeUserAgent string
var encodedUserAgent string

// SetHTTPClient overrides the default HTTP client.
// This is useful if you're running in a Google AppEngine environment
// where the http.DefaultClient is not available.
func SetHTTPClient(client *http.Client) {
	httpClient = client
}

// NewBackends creates a new set of backends with the given HTTP client. You
// should only need to use this for testing purposes or on App Engine.
func NewBackends(httpClient *http.Client) *Backends {
	return &Backends{
		API: BackendConfiguration{
			APIBackend, APIURL, httpClient},
		Uploads: BackendConfiguration{
			UploadsBackend, UploadsURL, httpClient},
	}
}

// GetBackend returns the currently used backend in the binding.
func GetBackend(backend SupportedBackend) Backend {
	var ret Backend
	switch backend {
	case APIBackend:
		if backends.API == nil {
			backends.API = BackendConfiguration{backend, apiURL, httpClient}
		}

		ret = backends.API
	case UploadsBackend:
		if backends.Uploads == nil {
			backends.Uploads = BackendConfiguration{backend, uploadsURL, httpClient}
		}
		ret = backends.Uploads
	}

	return ret
}

// SetBackend sets the backend used in the binding.
func SetBackend(backend SupportedBackend, b Backend) {
	switch backend {
	case APIBackend:
		backends.API = b
	case UploadsBackend:
		backends.Uploads = b
	}
}

// Call is the Backend.Call implementation for invoking Stripe APIs.
func (s BackendConfiguration) Call(method, path, key string, form *RequestValues, params *Params, v interface{}) error {
	var body io.Reader
	if form != nil && !form.Empty() {
		data := form.Encode()
		if strings.ToUpper(method) == "GET" {
			path += "?" + data
		} else {
			body = bytes.NewBufferString(data)
		}
	}

	req, err := s.NewRequest(method, path, key, "application/x-www-form-urlencoded", body, params)
	if err != nil {
		return err
	}

	if err := s.Do(req, v); err != nil {
		return err
	}

	return nil
}

// CallMultipart is the Backend.CallMultipart implementation for invoking Stripe APIs.
func (s BackendConfiguration) CallMultipart(method, path, key, boundary string, body io.Reader, params *Params, v interface{}) error {
	contentType := "multipart/form-data; boundary=" + boundary

	req, err := s.NewRequest(method, path, key, contentType, body, params)
	if err != nil {
		return err
	}

	if err := s.Do(req, v); err != nil {
		return err
	}

	return nil
}

// NewRequest is used by Call to generate an http.Request. It handles encoding
// parameters and attaching the appropriate headers.
func (s *BackendConfiguration) NewRequest(method, path, key, contentType string, body io.Reader, params *Params) (*http.Request, error) {
	if !strings.HasPrefix(path, "/") {
		path = "/" + path
	}

	path = s.URL + path

	req, err := http.NewRequest(method, path, body)
	if err != nil {
		if LogLevel > 0 {
			Logger.Printf("Cannot create Stripe request: %v\n", err)
		}
		return nil, err
	}

	req.SetBasicAuth(key, "")

	if params != nil {
		if idempotency := strings.TrimSpace(params.IdempotencyKey); idempotency != "" {
			if len(idempotency) > 255 {
				return nil, errors.New("Cannot use an IdempotencyKey longer than 255 characters long.")
			}

			req.Header.Add("Idempotency-Key", idempotency)
		}

		// Support the value of the old Account field for now.
		if account := strings.TrimSpace(params.Account); account != "" {
			req.Header.Add("Stripe-Account", account)
		}

		// But prefer StripeAccount.
		if stripeAccount := strings.TrimSpace(params.StripeAccount); stripeAccount != "" {
			req.Header.Add("Stripe-Account", stripeAccount)
		}
	}

	req.Header.Add("Stripe-Version", apiversion)
	req.Header.Add("User-Agent", encodedUserAgent)
	req.Header.Add("Content-Type", contentType)
	req.Header.Add("X-Stripe-Client-User-Agent", encodedStripeUserAgent)

	return req, nil
}

// Do is used by Call to execute an API request and parse the response. It uses
// the backend's HTTP client to execute the request and unmarshals the response
// into v. It also handles unmarshaling errors returned by the API.
func (s *BackendConfiguration) Do(req *http.Request, v interface{}) error {
	if LogLevel > 1 {
		Logger.Printf("Requesting %v %v%v\n", req.Method, req.URL.Host, req.URL.Path)
	}

	start := time.Now()

	res, err := s.HTTPClient.Do(req)

	if LogLevel > 2 {
		Logger.Printf("Completed in %v\n", time.Since(start))
	}

	if err != nil {
		if LogLevel > 0 {
			Logger.Printf("Request to Stripe failed: %v\n", err)
		}
		return err
	}
	defer res.Body.Close()

	resBody, err := ioutil.ReadAll(res.Body)
	if err != nil {
		if LogLevel > 0 {
			Logger.Printf("Cannot parse Stripe response: %v\n", err)
		}
		return err
	}

	if res.StatusCode >= 400 {
		return s.ResponseToError(res, resBody)
	}

	if LogLevel > 2 {
		Logger.Printf("Stripe Response: %q\n", resBody)
	}

	if v != nil {
		return json.Unmarshal(resBody, v)
	}

	return nil
}

func (s *BackendConfiguration) ResponseToError(res *http.Response, resBody []byte) error {
	// for some odd reason, the Erro structure doesn't unmarshal
	// initially I thought it was because it's a struct inside of a struct
	// but even after trying that, it still didn't work
	// so unmarshalling to a map for now and parsing the results manually
	// but should investigate later
	var errMap map[string]interface{}
	json.Unmarshal(resBody, &errMap)

	e, ok := errMap["error"]
	if !ok {
		err := errors.New(string(resBody))
		if LogLevel > 0 {
			Logger.Printf("Unparsable error returned from Stripe: %v\n", err)
		}
		return err
	}

	root := e.(map[string]interface{})

	stripeErr := &Error{
		Type:           ErrorType(root["type"].(string)),
		Msg:            root["message"].(string),
		HTTPStatusCode: res.StatusCode,
		RequestID:      res.Header.Get("Request-Id"),
	}

	if code, ok := root["code"]; ok {
		stripeErr.Code = ErrorCode(code.(string))
	}

	if param, ok := root["param"]; ok {
		stripeErr.Param = param.(string)
	}

	if charge, ok := root["charge"]; ok {
		stripeErr.ChargeID = charge.(string)
	}

	switch stripeErr.Type {
	case ErrorTypeAPI:
		stripeErr.Err = &APIError{stripeErr: stripeErr}

	case ErrorTypeAPIConnection:
		stripeErr.Err = &APIConnectionError{stripeErr: stripeErr}

	case ErrorTypeAuthentication:
		stripeErr.Err = &AuthenticationError{stripeErr: stripeErr}

	case ErrorTypeCard:
		cardErr := &CardError{stripeErr: stripeErr}
		stripeErr.Err = cardErr

		if declineCode, ok := root["decline_code"]; ok {
			cardErr.DeclineCode = declineCode.(string)
		}

	case ErrorTypeInvalidRequest:
		stripeErr.Err = &InvalidRequestError{stripeErr: stripeErr}

	case ErrorTypePermission:
		stripeErr.Err = &PermissionError{stripeErr: stripeErr}

	case ErrorTypeRateLimit:
		stripeErr.Err = &RateLimitError{stripeErr: stripeErr}
	}

	if LogLevel > 0 {
		Logger.Printf("Error encountered from Stripe: %v\n", stripeErr)
	}

	return stripeErr
}

// SetAppInfo sets app information. See AppInfo.
func SetAppInfo(info *AppInfo) {
	if info != nil && info.Name == "" {
		panic(fmt.Errorf("App info name cannot be empty"))
	}
	appInfo = info

	// This is run in init, but we need to reinitialize it now that we have
	// some app info.
	initUserAgent()
}

// getUname tries to get a uname from the system, but not that hard. It tries
// to execute `uname -a`, but swallows any errors in case that didn't work
// (i.e. non-Unix non-Mac system or some other reason).
func getUname() string {
	path, err := exec.LookPath("uname")
	if err != nil {
		return UnknownPlatform
	}

	cmd := exec.Command(path, "-a")
	var out bytes.Buffer
	cmd.Stderr = nil // goes to os.DevNull
	cmd.Stdout = &out
	err = cmd.Run()
	if err != nil {
		return UnknownPlatform
	}

	return out.String()
}

func initUserAgent() {
	encodedUserAgent = "Stripe/v1 GoBindings/" + clientversion
	if appInfo != nil {
		encodedUserAgent += " " + appInfo.formatUserAgent()
	}

	stripeUserAgent := &stripeClientUserAgent{
		Application:     appInfo,
		BindingsVersion: clientversion,
		Language:        "go",
		LanguageVersion: runtime.Version(),
		Publisher:       "stripe",
		Uname:           getUname(),
	}
	marshaled, err := json.Marshal(stripeUserAgent)
	// Encoding this struct should never be a problem, so we're okay to panic
	// in case it is for some reason.
	if err != nil {
		panic(err)
	}
	encodedStripeUserAgent = string(marshaled)
}
<|endoftext|>


<commit_before>//  The MIT License (MIT) - http://opensource.org/licenses/MIT
//
//  Copyright (c) 2014 slowfei
//
//  Create on 2014-08-22
//  Update on 2014-11-26
//  Email  slowfei(#)foxmail.com
//  Home   http://www.slowfei.com

//
package gosfdoc

import (
	"bytes"
	"container/list"
	"github.com/slowfei/gosfcore/encoding/json"
	"github.com/slowfei/gosfcore/utils/filemanager"
	"io/ioutil"
	"os"
	"regexp"
)

var (
	_defaultAbout = []byte(`
## About
------

gosfdoc document generator

More references: [https://github.com/slowfei/gosfdoc][0]

The MIT license (MIT) - [http://opensource.org/licenses/MIT][1]

Copyright (c) 2014 slowfei

Email: slowfei#foxmail.com

[0]:https://github.com/slowfei/gosfdoc
[1]:http://opensource.org/licenses/MIT
`)

	_defaultIntro = []byte(`
##Document Introduction

Sorry! Document author did not write any information.

----
This is a good tool, Can help you make beautiful documents.

More references: [https://github.com/slowfei/gosfdoc][0]

[0]:https://github.com/slowfei/gosfdoc
`)
)

/**
 *  file content buffer
 */
type FileBuf struct {
	path     string
	fileInfo os.FileInfo
	buf      []byte
	rowsBuf  [][]byte
}

/**
 *  new file buffer
 *
 *  @param `fileContent`
 *  @param `path` file path
 *  @param `info` file info
 *  @param replace regexp, replace text to empty(''), call regexp.ReplaceAll func
 */
func NewFileBuf(fileContent []byte, path string, info os.FileInfo, filter *regexp.Regexp) *FileBuf {
	buf := new(FileBuf)
	if nil != filter {
		buf.buf = filter.ReplaceAll(fileContent, nil)
	} else {
		buf.buf = fileContent
	}

	if 0 != len(buf.buf) {
		buf.rowsBuf = bytes.Split(buf.buf, []byte("\n"))
	}

	buf.fileInfo = info
	buf.path = path
	return buf
}

/**
 *	out file
 *
 *	@param `path` out path
 */
func (f *FileBuf) WriteFilepath(path string) error {
	return SFFileManager.WirteFilepath(path, f.buf)
}

/**
 *  regexp find bytes
 *
 *  @param `rex`
 *  @return
 */
func (f *FileBuf) Find(rex *regexp.Regexp) []byte {
	return rex.Find(f.buf)
}

/**
 *  regexp find all bytes
 *
 *  @param `rex`
 *  @return
 */
func (f *FileBuf) FindAll(rex *regexp.Regexp) [][]byte {
	return rex.FindAll(f.buf, -1)
}

/**
 *	Regexp.FindAllSubmatch
 *
 *  @param `rex`
 *  @return
 */
func (f *FileBuf) FindAllSubmatch(rex *regexp.Regexp) [][][]byte {
	return rex.FindAllSubmatch(f.buf, -1)
}

/**
 *	get content line number
 *
 *	@param `rowCont` row content
 *	@return line number
 */
func (f *FileBuf) QueryLineNumber(rowCont []byte) int {
	result := -1
	aLen := len(rowCont)

	count := len(f.rowsBuf)
	for i := 0; i < count; i++ {
		rowBuf := f.rowsBuf[i]
		if aLen == len(rowBuf) {
			if bytes.Equal(rowCont, rowBuf) {
				result = i
				break
			}
		}
	}

	return result
}

/**
 *	each row content
 *
 *	@param `fc`	index : line number;
 *				rowCont : row content;
 *				return true is pass, false is stop;
 *
 */
func (f *FileBuf) EachRow(fc func(index int, rowCont []byte) bool) {
	if nil == fc {
		return
	}

	count := len(f.rowsBuf)
	for i := 0; i < count; i++ {
		rowCont := f.rowsBuf[i]
		if !fc(i, rowCont) {
			break
		}
	}
}

/**
 *	get row content by line number 0 start.
 *
 *	@param `lineNumber` line number
 *	@param	content of the specified line number
 */
func (f *FileBuf) RowByIndex(lineNumber int) []byte {
	var result []byte = nil
	count := len(f.rowsBuf)

	if 0 <= lineNumber && count > lineNumber {
		result = f.rowsBuf[lineNumber]
	}

	return result
}

/**
 *  get file path
 *
 *  @return
 */
func (f *FileBuf) Path() string {
	return f.path
}

/**
 *  get file info
 *
 *  @return
 */
func (f *FileBuf) FileInfo() os.FileInfo {
	return f.fileInfo
}

/**
 *  buffer to string
 *
 *  @return
 */
func (f *FileBuf) String() string {
	return string(f.buf)
}

/**
 *  source code file
 */
type CodeFile struct {
	parser      DocParser  // file parser
	docs        []Document // current file public documents
	FileCont    *FileBuf   // file buffer content
	PrivateDoc  bool       // if private document not output
	PrivateCode bool       // if private source code not output
}

/**
 *  source code file list
 */
type CodeFiles struct {
	files *list.List
}

/**
 *  new CodeFiles
 */
func NewCodeFiles() *CodeFiles {
	cf := new(CodeFiles)
	cf.files = list.New()
	return cf
}

/**
 *  add file
 *
 *  @param file
 */
func (c *CodeFiles) addFile(file CodeFile) {
	if nil == c.files {
		c.files = list.New()
	}
	c.files.PushBack(file)
}

/**
 *  each CodeFile
 *
 *  @param `f` func return true continue
 */
func (c *CodeFiles) Each(f func(file CodeFile) bool) {
	if nil == f {
		return
	}
	for e := c.files.Front(); e != nil; e = e.Next() {
		if !f(e.Value.(CodeFile)) {
			break
		}
	}
}

/**
 *	file list storage length
 *
 *	@return file number
 */
func (c *CodeFiles) FilesLen() int {
	return c.files.Len()
}

/**
 *  output `content.json`
 */
type ContentJson struct {
	HtmlTitle string // html document title
	DocTitle  string // html top show title
	MenuTitle string // html left menu title
}

/**
 *	output write file path
 */
func (c ContentJson) WriteFilepath(path string) error {
	json, err := SFJson.NewJson(c, "", "")
	if nil != err {
		return err
	}
	return json.WriteFilepath(path, true)
}

/**
 *	markdown about
 */
type About struct {
	Content []byte
}

/**
 *	new default about
 *
 *	@return pointer type
 */
func NewDefaultAbout() *About {
	return &About{Content: _defaultAbout}
}

/**
 *	output file
 *
 *	@param `path` output full path
 *	@return
 */
func (a *About) WriteFilepath(path string) error {
	if 0 == len(a.Content) {
		a.Content = _defaultAbout
	}
	return ioutil.WriteFile(path, a.Content, 0660)
}

/**
 *	markdown intro
 */
type Intro struct {
	Content []byte
}

/**
 *	new default intro
 *
 *	@return pointer type
 */
func NewDefaultIntro() *Intro {
	return &Intro{Content: _defaultIntro}
}

/**
 *	output file
 *
 *	@param `path` output full path
 *	@return
 */
func (c *Intro) WriteFilepath(path string) error {
	if 0 == len(c.Content) {
		c.Content = _defaultIntro
	}
	return ioutil.WriteFile(path, c.Content, 0660)
}

/**
 *  package info
 */
type PackageInfo struct {
	menuName string `json:"-"` // type belongs
	Name     string // package name plain text
	Desc     string // description plain text
}

type FileLink struct {
	menuName string `json:"-"` // type belongs
	Filename string // a tag show text
	Link     string // a tag link

}

/**
 *  document struct info
 */
type Document struct {
	SortTag int    // sort tag
	Title   string // title plain text
	Content string // markdown text or plain text
}

/**
 *  preview struct info
 */
type Preview struct {
	SortTag  string // sort tag
	Level    int    // hierarchy level show. 0 is >, 1 is >>, 3 is >>> ...(markdown syntax)
	ShowText string // show plain text
	Anchor   string // preferably unique, with the func link
	DescText string // markdown brief description or implement objects, can empty.
}

/**
 *  body code block struct
 */
type CodeBlock struct {
	SortTag        string // sort tag
	MenuTitle      string // left navigation menu title
	Title          string // function name or custom title
	Anchor         string // function anchor text.
	Desc           string // description markdown text or plain text
	Code           string // show code text
	CodeLang       string // source code lang type string
	SourceFileName string // source code file name
	FileLines      []int  // block where the file line [5,10] is L5-L10
}

/**
 *	Preview,CodeBlock,Document sort implement
 */
type SortSet struct {
	previews   []Preview
	documents  []Document
	codeBlocks []CodeBlock
}

/**
 *	sort Len() implement
 */
func (s SortSet) Len() int {

	if 0 != len(s.previews) {
		return len(s.previews)
	} else if 0 != len(s.documents) {
		return len(s.documents)
	} else if 0 != len(s.codeBlocks) {
		return len(s.codeBlocks)
	} else {
		return 0
	}

}

/**
 *	sort Less(...) implement
 */
func (s SortSet) Less(i, j int) bool {

	if 0 != len(s.previews) {
		return s.previews[i].SortTag < s.previews[j].SortTag
	} else if 0 != len(s.documents) {
		return s.documents[i].SortTag < s.documents[j].SortTag
	} else if 0 != len(s.codeBlocks) {
		return s.codeBlocks[i].SortTag < s.codeBlocks[j].SortTag
	} else {
		return false
	}

}

/**
 *	sort Swap(...) implement
 */
func (s SortSet) Swap(i, j int) {

	if 0 != len(s.previews) {
		s.previews[i], s.previews[j] = s.previews[j], s.previews[i]
	} else if 0 != len(s.documents) {
		s.documents[i], s.documents[j] = s.documents[j], s.documents[i]
	} else if 0 != len(s.codeBlocks) {
		s.codeBlocks[i], s.codeBlocks[j] = s.codeBlocks[j], s.codeBlocks[i]
	}

}
<commit_msg>修改：修改行号处理的相关函数<commit_after>//  The MIT License (MIT) - http://opensource.org/licenses/MIT
//
//  Copyright (c) 2014 slowfei
//
//  Create on 2014-08-22
//  Update on 2014-12-08
//  Email  slowfei(#)foxmail.com
//  Home   http://www.slowfei.com

//
package gosfdoc

import (
	"container/list"
	"github.com/slowfei/gosfcore/encoding/json"
	"github.com/slowfei/gosfcore/utils/filemanager"
	"io/ioutil"
	"os"
	"regexp"
)

var (
	_defaultAbout = []byte(`
## About
------

gosfdoc document generator

More references: [https://github.com/slowfei/gosfdoc][0]

The MIT license (MIT) - [http://opensource.org/licenses/MIT][1]

Copyright (c) 2014 slowfei

Email: slowfei#foxmail.com

[0]:https://github.com/slowfei/gosfdoc
[1]:http://opensource.org/licenses/MIT
`)

	_defaultIntro = []byte(`
##Document Introduction

Sorry! Document author did not write any information.

----
This is a good tool, Can help you make beautiful documents.

More references: [https://github.com/slowfei/gosfdoc][0]

[0]:https://github.com/slowfei/gosfdoc
`)
)

/**
 *  file content buffer
 */
type FileBuf struct {
	path       string
	fileInfo   os.FileInfo
	buf        []byte
	lineLenSum []int // 记录每行长度的总和
}

/**
 *  new file buffer
 *
 *  @param `fileContent`
 *  @param `path` file path
 *  @param `info` file info
 *  @param replace regexp, replace text to empty(''), call regexp.ReplaceAll func
 */
func NewFileBuf(fileContent []byte, path string, info os.FileInfo, filter *regexp.Regexp) *FileBuf {
	buf := new(FileBuf)
	if nil != filter {
		buf.buf = filter.ReplaceAll(fileContent, nil)
	} else {
		buf.buf = fileContent
	}

	if 0 != len(buf.buf) {
		buf.lineLenSum = make([]int, 0, 0)
		count := len(buf.buf)
		for i := 0; i < count; i++ {
			b := buf.buf[i]
			if '\n' == b {
				buf.lineLenSum = append(buf.lineLenSum, i)
			}
		}
		//	add end line
		buf.lineLenSum = append(buf.lineLenSum, count)
	}

	buf.fileInfo = info
	buf.path = path
	return buf
}

/**
 *	out file
 *
 *	@param `path` out path
 */
func (f *FileBuf) WriteFilepath(path string) error {
	return SFFileManager.WirteFilepath(path, f.buf)
}

/**
 *  regexp find bytes
 *
 *  @param `rex`
 *  @return
 */
func (f *FileBuf) Find(rex *regexp.Regexp) []byte {
	return rex.Find(f.buf)
}

/**
 *  regexp find all bytes
 *
 *  @param `rex`
 *  @return
 */
func (f *FileBuf) FindAll(rex *regexp.Regexp) [][]byte {
	return rex.FindAll(f.buf, -1)
}

/**
 *	Regexp.FindAllSubmatch
 *
 *  @param `rex`
 *  @return
 */
func (f *FileBuf) FindAllSubmatch(rex *regexp.Regexp) [][][]byte {
	return rex.FindAllSubmatch(f.buf, -1)
}

/**
 *	Regexp.FindAllSubmatchIndex
 *
 *	@param `rex`
 *	@return
 */
func (f *FileBuf) FindAllSubmatchIndex(rex *regexp.Regexp) [][]int {
	return rex.FindAllSubmatchIndex(f.buf, -1)
}

/**
 *	line number by begin and end index
 *
 *	@param `beginIndex` buffer byte begin index
 *	@param `endIndex`	end index
 *	@return []int [start line,end line], line number 1 start.
 */
func (f *FileBuf) LineNumberByIndex(beginIndex, endIndex int) []int {
	result := []int{-1, -1}

	if endIndex > beginIndex {
		beginIndex, endIndex = endIndex, beginIndex
	}

	isBegin := false
	isEnd := false

	/*
		text:
		abcde  [a=0,b=1,c=2,d=3,e=4,\n=5]
		fghij  [f=6,g=8,h=9,i=10,j=11]

	*/

	lineLen := len(f.lineLenSum)
	for i := 0; i < lineLen; i++ {
		lineSum := f.lineLenSum[i]

		if !isBegin && lineSum >= beginIndex {
			result[0] = i + 1
			isBegin = true
		}

		if !isEnd && lineSum >= endIndex {
			result[1] = i + 1
			isEnd = true
		}

		if isBegin && isEnd {
			break
		}
	}

	if !isBegin || !isEnd {
		result[0] = -1
		result[1] = -1
	}

	return result
}

/**
 *	get row content by line number 1 start.
 *
 *	@param `lineNumber` line number
 *	@param	content of the specified line number
 */
func (f *FileBuf) RowByIndex(lineNumber int) []byte {
	var result []byte = nil
	lineLen := len(f.lineLenSum)

	if 0 >= lineNumber {
		lineNumber = 1
	}

	if 0 < lineLen && lineLen >= lineNumber {
		realIndex := lineNumber - 1
		upIndex := realIndex - 1 // 上一行下标，

		/*
			text:
			12345
			67890

			line length sum:
			[0] = 5 = len("12345\n")
			[1] = 11 = len("67890")

			lineNumber = 2; 获取第二行数据 buf[5+1,11]; (5+1)除去上一节点的换行
		*/

		startIndex := 0
		endIndex := f.lineLenSum[realIndex]

		if 0 <= upIndex {
			startIndex = f.lineLenSum[upIndex] + 1
		}

		result = f.SubBytes(startIndex, endIndex)
	}

	return result
}

/**
 *	extracts the file buffer from a bytes
 *
 *	@param `beginIndex`
 *	@param `endIndex`
 *	@return bytes
 */
func (f *FileBuf) SubBytes(beginIndex, endIndex int) []byte {
	if beginIndex > endIndex {
		return nil
	}

	if 0 > beginIndex {
		beginIndex = 0
	}

	if beginIndex == endIndex {
		endIndex = endIndex + 1
	}

	bufLen := len(f.buf)
	if bufLen < endIndex {
		endIndex = bufLen
	}

	result := f.buf[beginIndex:endIndex]
	return result
}

/**
 *  get file path
 *
 *  @return
 */
func (f *FileBuf) Path() string {
	return f.path
}

/**
 *  get file info
 *
 *  @return
 */
func (f *FileBuf) FileInfo() os.FileInfo {
	return f.fileInfo
}

/**
 *  buffer to string
 *
 *  @return
 */
func (f *FileBuf) String() string {
	return string(f.buf)
}

/**
 *  source code file
 */
type CodeFile struct {
	parser      DocParser  // file parser
	docs        []Document // current file public documents
	FileCont    *FileBuf   // file buffer content
	PrivateDoc  bool       // if private document not output
	PrivateCode bool       // if private source code not output
}

/**
 *  source code file list
 */
type CodeFiles struct {
	files *list.List
}

/**
 *  new CodeFiles
 */
func NewCodeFiles() *CodeFiles {
	cf := new(CodeFiles)
	cf.files = list.New()
	return cf
}

/**
 *  add file
 *
 *  @param file
 */
func (c *CodeFiles) addFile(file CodeFile) {
	if nil == c.files {
		c.files = list.New()
	}
	c.files.PushBack(file)
}

/**
 *  each CodeFile
 *
 *  @param `f` func return true continue
 */
func (c *CodeFiles) Each(f func(file CodeFile) bool) {
	if nil == f {
		return
	}
	for e := c.files.Front(); e != nil; e = e.Next() {
		if !f(e.Value.(CodeFile)) {
			break
		}
	}
}

/**
 *	file list storage length
 *
 *	@return file number
 */
func (c *CodeFiles) FilesLen() int {
	return c.files.Len()
}

/**
 *  output `content.json`
 */
type ContentJson struct {
	HtmlTitle string // html document title
	DocTitle  string // html top show title
	MenuTitle string // html left menu title
}

/**
 *	output write file path
 */
func (c ContentJson) WriteFilepath(path string) error {
	json, err := SFJson.NewJson(c, "", "")
	if nil != err {
		return err
	}
	return json.WriteFilepath(path, true)
}

/**
 *	markdown about
 */
type About struct {
	Content []byte
}

/**
 *	new default about
 *
 *	@return pointer type
 */
func NewDefaultAbout() *About {
	return &About{Content: _defaultAbout}
}

/**
 *	output file
 *
 *	@param `path` output full path
 *	@return
 */
func (a *About) WriteFilepath(path string) error {
	if 0 == len(a.Content) {
		a.Content = _defaultAbout
	}
	return ioutil.WriteFile(path, a.Content, 0660)
}

/**
 *	markdown intro
 */
type Intro struct {
	Content []byte
}

/**
 *	new default intro
 *
 *	@return pointer type
 */
func NewDefaultIntro() *Intro {
	return &Intro{Content: _defaultIntro}
}

/**
 *	output file
 *
 *	@param `path` output full path
 *	@return
 */
func (c *Intro) WriteFilepath(path string) error {
	if 0 == len(c.Content) {
		c.Content = _defaultIntro
	}
	return ioutil.WriteFile(path, c.Content, 0660)
}

/**
 *  package info
 */
type PackageInfo struct {
	menuName string `json:"-"` // type belongs
	Name     string // package name plain text
	Desc     string // description plain text
}

type FileLink struct {
	menuName string `json:"-"` // type belongs
	Filename string // a tag show text
	Link     string // a tag link

}

/**
 *  document struct info
 */
type Document struct {
	SortTag int    // sort tag
	Title   string // title plain text
	Content string // markdown text or plain text
}

/**
 *  preview struct info
 */
type Preview struct {
	SortTag  string // sort tag
	Level    int    // hierarchy level show. 0 is >, 1 is >>, 3 is >>> ...(markdown syntax)
	ShowText string // show plain text
	Anchor   string // preferably unique, with the func link
	DescText string // markdown brief description or implement objects, can empty.
}

/**
 *  body code block struct
 */
type CodeBlock struct {
	SortTag        string // sort tag
	MenuTitle      string // left navigation menu title
	Title          string // function name or custom title
	Anchor         string // function anchor text.
	Desc           string // description markdown text or plain text
	Code           string // show code text
	CodeLang       string // source code lang type string
	SourceFileName string // source code file name
	FileLines      []int  // block where the file line [5,10] is L5-L10
}

/**
 *	Preview,CodeBlock,Document sort implement
 */
type SortSet struct {
	previews   []Preview
	documents  []Document
	codeBlocks []CodeBlock
}

/**
 *	sort Len() implement
 */
func (s SortSet) Len() int {

	if 0 != len(s.previews) {
		return len(s.previews)
	} else if 0 != len(s.documents) {
		return len(s.documents)
	} else if 0 != len(s.codeBlocks) {
		return len(s.codeBlocks)
	} else {
		return 0
	}

}

/**
 *	sort Less(...) implement
 */
func (s SortSet) Less(i, j int) bool {

	if 0 != len(s.previews) {
		return s.previews[i].SortTag < s.previews[j].SortTag
	} else if 0 != len(s.documents) {
		return s.documents[i].SortTag < s.documents[j].SortTag
	} else if 0 != len(s.codeBlocks) {
		return s.codeBlocks[i].SortTag < s.codeBlocks[j].SortTag
	} else {
		return false
	}

}

/**
 *	sort Swap(...) implement
 */
func (s SortSet) Swap(i, j int) {

	if 0 != len(s.previews) {
		s.previews[i], s.previews[j] = s.previews[j], s.previews[i]
	} else if 0 != len(s.documents) {
		s.documents[i], s.documents[j] = s.documents[j], s.documents[i]
	} else if 0 != len(s.codeBlocks) {
		s.codeBlocks[i], s.codeBlocks[j] = s.codeBlocks[j], s.codeBlocks[i]
	}

}
<|endoftext|>