本文整理汇总了Golang中go/skia/org/infra/go/util.NewTimeoutClient函数的典型用法代码示例。如果您正苦于以下问题:Golang NewTimeoutClient函数的具体用法?Golang NewTimeoutClient怎么用?Golang NewTimeoutClient使用的例子?那么恭喜您, 这里精选的函数代码示例或许可以为您提供帮助。
在下文中一共展示了NewTimeoutClient函数的18个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于我们的系统推荐出更棒的Golang代码示例。
示例1: startWorkers
// startWorkers creates a worker pool of numWorkers workers to make requests.
//
// Requests to make arrive over the targetCh channel, latency measurements in
// millis go out over the latencies channel, and wg is used to synchronize the
// workers' completion.
func startWorkers(targets <-chan Target, latencies chan<- float64, wg *sync.WaitGroup) error {
wg.Add(*numWorkers)
for i := 0; i < *numWorkers; i++ {
c := util.NewTimeoutClient()
go func(c *http.Client) {
for t := range targets {
t0 := time.Now()
resp, err := c.Get(t.URL)
t1 := time.Now()
if err != nil {
fmt.Printf("Failure for Get: %v %v\n", t.URL, err)
continue
}
// TODO(jcgregorio) Add stats for failures if we start seeing them.
if resp.StatusCode != t.Code {
fmt.Printf("Wrong status code expected %v, got %v at %v\n", t.Code, resp.StatusCode, t.URL)
}
duration := t1.Sub(t0)
latencies <- float64(duration.Nanoseconds() / 1000000)
}
wg.Done()
}(c)
}
return nil
}
开发者ID:kleopatra999,项目名称:skia-buildbot,代码行数:33,代码来源:main.go
示例2: UpdateWebappTaskV2
func UpdateWebappTaskV2(vars task_common.UpdateTaskVars) error {
postUrl := WebappRoot + vars.UriPath()
glog.Infof("Updating %v on %s", vars, postUrl)
json, err := json.Marshal(vars)
if err != nil {
return fmt.Errorf("Failed to marshal %v: %s", vars, err)
}
req, err := http.NewRequest("POST", postUrl, bytes.NewReader(json))
if err != nil {
return fmt.Errorf("Could not create HTTP request: %s", err)
}
hash, err := webhook.ComputeAuthHashBase64(json)
if err != nil {
return fmt.Errorf("Could not compute authentication hash: %s", err)
}
req.Header.Set(webhook.REQUEST_AUTH_HASH_HEADER, hash)
client := util.NewTimeoutClient()
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("Could not update webapp task: %s", err)
}
defer util.Close(resp.Body)
if resp.StatusCode != 200 {
response, _ := ioutil.ReadAll(resp.Body)
return fmt.Errorf("Could not update webapp task, response status code was %d: %s", resp.StatusCode, response)
}
return nil
}
开发者ID:saltmueller,项目名称:skia-buildbot,代码行数:29,代码来源:frontend.go
示例3: main
func main() {
common.InitWithMetrics("certpoller", graphiteServer)
client := util.NewTimeoutClient()
certs := []*cert{}
// Populate certs based on cmd-line args.
for _, metadata := range flag.Args() {
c := &cert{
metadata: metadata,
file: fileFromMetadata(metadata),
etag: "",
}
err := get(client, c)
if err != nil {
glog.Fatalf("Failed to retrieve the cert %s: %s", c, err)
}
certs = append(certs, c)
}
for _ = range time.Tick(30 * time.Minute) {
for _, c := range certs {
if err := get(client, c); err != nil {
glog.Errorf("Failed to update cert %s: %s", c.metadata, err)
}
}
}
}
开发者ID:kleopatra999,项目名称:skia-buildbot,代码行数:26,代码来源:main.go
示例4: UpdateWebappTask
func UpdateWebappTask(gaeTaskID int64, webappURL string, extraData map[string]string) error {
glog.Infof("Updating %s on %s with %s", gaeTaskID, webappURL, extraData)
pwdBytes, err := ioutil.ReadFile(ctutil.WebappPasswordPath)
if err != nil {
return fmt.Errorf("Could not read the webapp password file: %s", err)
}
pwd := strings.TrimSpace(string(pwdBytes))
postData := url.Values{}
postData.Set("key", strconv.FormatInt(gaeTaskID, 10))
postData.Add("password", pwd)
for k, v := range extraData {
postData.Add(k, v)
}
req, err := http.NewRequest("POST", webappURL, strings.NewReader(postData.Encode()))
if err != nil {
return fmt.Errorf("Could not create HTTP request: %s", err)
}
client := util.NewTimeoutClient()
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("Could not update webapp task: %s", err)
}
defer util.Close(resp.Body)
if resp.StatusCode != 200 {
return fmt.Errorf("Could not update webapp task, response status code was %d: %s", resp.StatusCode, err)
}
return nil
}
开发者ID:saltmueller,项目名称:skia-buildbot,代码行数:28,代码来源:frontend.go
示例5: init
func init() {
var err error
st, err = storage.New(util.NewTimeoutClient())
if err != nil {
panic("Can't construct HTTP client")
}
ingester.Register(config.CONSTRUCTOR_NANO_TRYBOT, NewTrybotResultIngester)
}
开发者ID:saltmueller,项目名称:skia-buildbot,代码行数:9,代码来源:trybot.go
示例6: NewBranchTileBuilder
// NewBranchTileBuilder returns an instance of BranchTileBuilder that allows
// creating tiles based on the given VCS or code review system based on
// querying db.
//
// TODO(stephana): The EventBus is used to update the internal cache as commits are updated.
func NewBranchTileBuilder(db DB, git *gitinfo.GitInfo, reviewURL string, evt *eventbus.EventBus) BranchTileBuilder {
return &tileBuilder{
db: db,
vcs: git,
review: rietveld.New(reviewURL, util.NewTimeoutClient()),
reviewURL: reviewURL,
cache: lru.New(MAX_ISSUE_CACHE_SIZE),
tcache: lru.New(MAX_TILE_CACHE_SIZE),
}
}
开发者ID:saltmueller,项目名称:skia-buildbot,代码行数:15,代码来源:branch.go
示例7: NewFileDiffStore
// NewFileDiffStore intializes and returns a file based implementation of
// DiffStore. The optional http.Client is used to make HTTP requests to Google
// Storage. If nil is supplied then a default client is used. The baseDir is
// the local base directory where the DEFAULT_IMG_DIR_NAME,
// DEFAULT_DIFF_DIR_NAME and the DEFAULT_DIFFMETRICS_DIR_NAME directories
// exist. gsBucketName is the bucket images will be downloaded from.
// storageBaseDir is the directory in the bucket (if empty
// DEFAULT_GS_IMG_DIR_NAME is used). workerPoolSize is the max number of
// simultaneous goroutines that will be created when running Get or AbsPath.
// Use RECOMMENDED_WORKER_POOL_SIZE if unsure what this value should be.
func NewFileDiffStore(client *http.Client, baseDir, gsBucketName string, storageBaseDir string, cacheFactory CacheFactory, workerPoolSize int) (diff.DiffStore, error) {
if client == nil {
client = util.NewTimeoutClient()
}
if storageBaseDir == "" {
storageBaseDir = DEFAULT_GS_IMG_DIR_NAME
}
imageCache, err := lru.New(IMAGE_LRU_CACHE_SIZE)
if err != nil {
return nil, fmt.Errorf("Unable to alloace image LRU cache: %s", err)
}
diffCache := cacheFactory("di", DiffMetricsCodec(0))
unavailableChan := make(chan *diff.DigestFailure, 10)
statusDir := fileutil.Must(fileutil.EnsureDirExists(filepath.Join(baseDir, DEFAULT_STATUS_DIR_NAME)))
failureDB, err := bolt.Open(filepath.Join(statusDir, FAILUREDB_NAME), 0600, nil)
if err != nil {
return nil, fmt.Errorf("Unable to open failuredb: %s", err)
}
fs := &FileDiffStore{
client: client,
localImgDir: fileutil.Must(fileutil.EnsureDirExists(filepath.Join(baseDir, DEFAULT_IMG_DIR_NAME))),
localDiffDir: fileutil.Must(fileutil.EnsureDirExists(filepath.Join(baseDir, DEFAULT_DIFF_DIR_NAME))),
localDiffMetricsDir: fileutil.Must(fileutil.EnsureDirExists(filepath.Join(baseDir, DEFAULT_DIFFMETRICS_DIR_NAME))),
localTempFileDir: fileutil.Must(fileutil.EnsureDirExists(filepath.Join(baseDir, DEFAULT_TEMPFILE_DIR_NAME))),
gsBucketName: gsBucketName,
storageBaseDir: storageBaseDir,
imageCache: imageCache,
diffCache: diffCache,
unavailableDigests: map[string]*diff.DigestFailure{},
unavailableChan: unavailableChan,
failureDB: failureDB,
}
if err := fs.loadDigestFailures(); err != nil {
return nil, err
}
go func() {
for {
digestFailure := <-unavailableChan
if err := fs.addDigestFailure(digestFailure); err != nil {
glog.Errorf("Unable to store digest failure: %s", err)
} else if err = fs.loadDigestFailures(); err != nil {
glog.Errorf("Unable to load failures: %s", err)
}
}
}()
fs.activateWorkers(workerPoolSize)
return fs, nil
}
开发者ID:saltmueller,项目名称:skia-buildbot,代码行数:65,代码来源:filediffstore.go
示例8: TestAreTimestampsEqual
func TestAreTimestampsEqual(t *testing.T) {
gs, err := NewGsUtil(util.NewTimeoutClient())
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
tmpDir := filepath.Join(os.TempDir(), "util_test")
util.Mkdir(tmpDir, 0777)
defer util.RemoveAll(tmpDir)
f, err := os.Create(filepath.Join(tmpDir, TIMESTAMP_FILE_NAME))
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
defer util.Close(f)
// Test with matching timestamps.
if _, err := f.WriteString(GS_TEST_TIMESTAMP_VALUE); err != nil {
t.Errorf("Unexpected error: %s", err)
}
result1, err := gs.AreTimeStampsEqual(tmpDir, "unit-tests/util/")
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
assert.True(t, result1)
// Test with differing timestamps.
if _, err := f.WriteString(GS_TEST_TIMESTAMP_VALUE); err != nil {
t.Errorf("Unexpected error: %s", err)
}
result2, err := gs.AreTimeStampsEqual(tmpDir, "unit-tests/util/")
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
assert.False(t, result2)
// Test with Google Storage timestamp missing.
result3, err := gs.AreTimeStampsEqual(tmpDir, "unit-tests/util/dummy_name/")
if err == nil {
t.Error("Expected an error")
}
assert.False(t, result3)
// Test with local timestamp missing.
result4, err := gs.AreTimeStampsEqual(tmpDir+"dummy_name", "unit-tests/util/")
if err == nil {
t.Error("Expected an error")
}
assert.False(t, result4)
}
开发者ID:Tiger66639,项目名称:skia-buildbot,代码行数:50,代码来源:gs_test.go
示例9: NewFileDiffStore
// NewFileDiffStore intializes and returns a file based implementation of
// DiffStore. The optional http.Client is used to make HTTP requests to Google
// Storage. If nil is supplied then a default client is used. The baseDir is the
// local base directory where the DEFAULT_IMG_DIR_NAME, DEFAULT_DIFF_DIR_NAME and
// the DEFAULT_DIFFMETRICS_DIR_NAME directories exist. gsBucketName is the bucket
// images will be downloaded from. storageBaseDir is the directory in the
// bucket (if empty DEFAULT_GS_IMG_DIR_NAME is used).
// workerPoolSize is the max number of simultaneous goroutines that will be
// created when running Get or AbsPath.
// Use RECOMMENDED_WORKER_POOL_SIZE if unsure what this value should be.
func NewFileDiffStore(client *http.Client, baseDir, gsBucketName string, storageBaseDir string, cacheFactory CacheFactory, workerPoolSize int) (diff.DiffStore, error) {
if client == nil {
client = util.NewTimeoutClient()
}
if storageBaseDir == "" {
storageBaseDir = DEFAULT_GS_IMG_DIR_NAME
}
imageCache, err := lru.New(IMAGE_LRU_CACHE_SIZE)
if err != nil {
return nil, fmt.Errorf("Unable to alloace image LRU cache: %s", err)
}
diffCache := cacheFactory("di", DiffMetricsCodec(0))
unavailableChan := make(chan string, 10)
fs := &FileDiffStore{
client: client,
localImgDir: fileutil.Must(fileutil.EnsureDirExists(filepath.Join(baseDir, DEFAULT_IMG_DIR_NAME))),
localDiffDir: fileutil.Must(fileutil.EnsureDirExists(filepath.Join(baseDir, DEFAULT_DIFF_DIR_NAME))),
localDiffMetricsDir: fileutil.Must(fileutil.EnsureDirExists(filepath.Join(baseDir, DEFAULT_DIFFMETRICS_DIR_NAME))),
localTempFileDir: fileutil.Must(fileutil.EnsureDirExists(filepath.Join(baseDir, DEFAULT_TEMPFILE_DIR_NAME))),
gsBucketName: gsBucketName,
storageBaseDir: storageBaseDir,
imageCache: imageCache,
diffCache: diffCache,
unavailableDigests: map[string]bool{},
unavailableChan: unavailableChan,
}
// TODO(stephana): Clean this up and store digests to ignore in the
// database and expose them on the front-end.
// This is the hash of the empty, we should ignore this right away.
unavailableChan <- "d41d8cd98f00b204e9800998ecf8427e"
go func() {
var ignoreDigest string
for {
ignoreDigest = <-unavailableChan
func() {
fs.unavailableMutex.Lock()
defer fs.unavailableMutex.Unlock()
fs.unavailableDigests[ignoreDigest] = true
}()
}
}()
fs.activateWorkers(workerPoolSize)
return fs, nil
}
开发者ID:kleopatra999,项目名称:skia-buildbot,代码行数:60,代码来源:filediffstore.go
示例10: NewBuilder
func NewBuilder(git *gitinfo.GitInfo, address string, tileSize int, traceBuilder tiling.TraceBuilder) (*Builder, error) {
review := rietveld.New(rietveld.RIETVELD_SKIA_URL, util.NewTimeoutClient())
builder, err := db.NewBuilder(git, address, tileSize, traceBuilder)
if err != nil {
return nil, fmt.Errorf("Failed to construct Builder: %s", err)
}
return &Builder{
Builder: builder,
vcs: git,
review: review,
cache: map[string]*rietveld.Issue{},
}, nil
}
开发者ID:1394,项目名称:skia-buildbot,代码行数:14,代码来源:perftrace.go
示例11: openUri
func openUri(uriPath string) (*http.Response, error) {
uri := GS_TEST_DATA_ROOT_URI + uriPath
client := util.NewTimeoutClient()
request, err := gs.RequestForStorageURL(uri)
if err != nil {
return nil, err
}
resp, err := client.Do(request)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("Downloading %s failed. Got response status: %d", uri, resp.StatusCode)
}
return resp, nil
}
开发者ID:saltmueller,项目名称:skia-buildbot,代码行数:19,代码来源:testutils.go
示例12: get
// get retrieves the named value from the Metadata server. See
// https://developers.google.com/compute/docs/metadata
//
// level should be either "instance" or "project" for the kind of
// metadata to retrieve.
func get(name string, level string) (string, error) {
req, err := http.NewRequest("GET", "http://metadata/computeMetadata/v1/"+level+"/attributes/"+name, nil)
if err != nil {
return "", fmt.Errorf("metadata.Get() failed to build request: %s", err)
}
c := util.NewTimeoutClient()
req.Header.Add("Metadata-Flavor", "Google")
resp, err := c.Do(req)
if err != nil {
return "", fmt.Errorf("metadata.Get() failed to make HTTP request for %s: %s", name, err)
}
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("HTTP response has status %d", resp.StatusCode)
}
defer util.Close(resp.Body)
value, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("Failed to read %s from metadata server: %s", name, err)
}
return string(value), nil
}
开发者ID:Tiger66639,项目名称:skia-buildbot,代码行数:26,代码来源:metadata.go
示例13: main
func main() {
defer common.LogPanic()
common.Init()
client := util.NewTimeoutClient()
retVal := 255
// Populate certs based on cmd-line args.
for _, metadata := range flag.Args() {
c := &cert{
metadata: metadata,
file: fileFromMetadata(metadata),
etag: "",
}
err := get(client, c)
if err != nil {
glog.Fatalf("Failed to retrieve the cert %s: %s", c, err)
}
retVal = 0
}
os.Exit(retVal)
}
开发者ID:saltmueller,项目名称:skia-buildbot,代码行数:22,代码来源:main.go
示例14: ReloadTemplates
"github.com/gorilla/mux"
"github.com/skia-dev/glog"
"go.skia.org/infra/ct/go/ctfe/task_common"
ctfeutil "go.skia.org/infra/ct/go/ctfe/util"
"go.skia.org/infra/ct/go/db"
ctutil "go.skia.org/infra/ct/go/util"
skutil "go.skia.org/infra/go/util"
"go.skia.org/infra/go/webhook"
)
var (
addTaskTemplate *template.Template = nil
runsHistoryTemplate *template.Template = nil
httpClient = skutil.NewTimeoutClient()
)
func ReloadTemplates(resourcesDir string) {
addTaskTemplate = template.Must(template.ParseFiles(
filepath.Join(resourcesDir, "templates/chromium_perf.html"),
filepath.Join(resourcesDir, "templates/header.html"),
filepath.Join(resourcesDir, "templates/titlebar.html"),
))
runsHistoryTemplate = template.Must(template.ParseFiles(
filepath.Join(resourcesDir, "templates/chromium_perf_runs_history.html"),
filepath.Join(resourcesDir, "templates/header.html"),
filepath.Join(resourcesDir, "templates/titlebar.html"),
))
}
开发者ID:saltmueller,项目名称:skia-buildbot,代码行数:30,代码来源:chromium_perf.go
示例15: NewIssueTracker
func NewIssueTracker(apiKey string) IssueTracker {
return &CodesiteIssueTracker{
apiKey: apiKey,
client: util.NewTimeoutClient(),
}
}
开发者ID:kleopatra999,项目名称:skia-buildbot,代码行数:6,代码来源:issues.go
示例16: NewClient
// NewClient creates a new client for interacting with Rietveld.
func NewClient() *Reitveld {
return &Reitveld{
client: util.NewTimeoutClient(),
}
}
开发者ID:saltmueller,项目名称:skia-buildbot,代码行数:6,代码来源:reitveld.go
示例17: TestPertTrace
func TestPertTrace(t *testing.T) {
b, err := ioutil.ReadFile(filepath.Join("testdata", "rietveld_response.txt"))
assert.Nil(t, err)
m := mockhttpclient.NewURLMock()
// Mock this only once to confirm that caching works.
m.MockOnce("https://codereview.chromium.org/api/1490543002", b)
review := rietveld.New(rietveld.RIETVELD_SKIA_URL, util.NewTimeoutClient())
vcsCommits := []*vcsinfo.LongCommit{
&vcsinfo.LongCommit{
ShortCommit: &vcsinfo.ShortCommit{
Hash: "foofoofoo",
Author: "[email protected]",
Subject: "some commit",
},
},
}
vcs := ingestion.MockVCS(vcsCommits)
builder := &Builder{
Builder: nil,
vcs: vcs,
review: review,
cache: map[string]*rietveld.Issue{},
}
commits := []*db.CommitID{
&db.CommitID{
Timestamp: time.Now(),
ID: "1",
Source: "https://codereview.chromium.org/1490543002",
},
&db.CommitID{
Timestamp: time.Now(),
ID: "foofoofoo",
Source: "master",
},
}
long := builder.convertToLongCommits(commits, "master")
assert.Equal(t, 1, len(long), "Only one commit should match master.")
assert.Equal(t, "foofoofoo", long[0].ID)
assert.Equal(t, "some commit", long[0].Desc)
assert.Equal(t, "[email protected]", long[0].Author)
long = builder.convertToLongCommits(commits, "https://codereview.chromium.org/1490543002")
assert.Equal(t, 1, len(long), "Only one commit should match the trybot.")
assert.Equal(t, "1", long[0].ID)
assert.Equal(t, "no merge conflicts here.", long[0].Desc)
assert.Equal(t, "jcgregorio", long[0].Author)
long = builder.convertToLongCommits(commits, "")
assert.Equal(t, 2, len(long), "Both commits should now appear.")
assert.Equal(t, "1", long[0].ID)
assert.Equal(t, "no merge conflicts here.", long[0].Desc)
assert.Equal(t, "jcgregorio", long[0].Author)
assert.Equal(t, "foofoofoo", long[1].ID)
assert.Equal(t, "some commit", long[1].Desc)
assert.Equal(t, "[email protected]", long[1].Author)
badCommits := []*db.CommitID{
&db.CommitID{
Timestamp: time.Now(),
ID: "2",
Source: "https://codereview.chromium.org/99999999",
},
&db.CommitID{
Timestamp: time.Now(),
ID: "barbarbar",
Source: "master",
},
}
long = builder.convertToLongCommits(badCommits, "")
assert.Equal(t, 2, len(long), "Both commits should now appear.")
assert.Equal(t, "2", long[0].ID)
assert.Equal(t, "", long[0].Desc)
assert.Equal(t, "", long[0].Author)
assert.Equal(t, "barbarbar", long[1].ID)
assert.Equal(t, "", long[1].Desc)
assert.Equal(t, "", long[1].Author)
}
开发者ID:1394,项目名称:skia-buildbot,代码行数:83,代码来源:perftrace_test.go
示例18: main
func main() {
defer common.LogPanic()
worker_common.Init()
defer util.TimeTrack(time.Now(), "Running Chromium Perf on Swarming")
defer glog.Flush()
// Validate required arguments.
if *chromiumBuild == "" {
glog.Error("Must specify --chromium_build")
return
}
if *benchmarkName == "" {
glog.Error("Must specify --benchmark_name")
return
}
if *telemetryBinariesDir == "" {
glog.Error("Must specify --telemetry_binaries_dir")
return
}
if *pageSetsDir == "" {
glog.Error("Must specify --page_sets_dir")
return
}
if *buildbotMaster == "" {
glog.Error("Must specify --master")
return
}
if *buildbotBuilder == "" {
glog.Error("Must specify --builder")
return
}
if *gitHash == "" {
glog.Error("Must specify --git_hash")
return
}
chromiumBinary := filepath.Join(*chromiumBuild, util.BINARY_CHROME)
// Establish output paths.
localOutputDir := util.BenchmarkRunsDir
skutil.MkdirAll(localOutputDir, 0700)
fileInfos, err := ioutil.ReadDir(*pageSetsDir)
if err != nil {
glog.Errorf("Unable to read the pagesets dir %s: %s", *pageSetsDir, err)
return
}
for _, fileInfo := range fileInfos {
if fileInfo.IsDir() {
continue
}
if err := runBenchmark(fileInfo.Name(), *pageSetsDir, localOutputDir, *chromiumBuild, chromiumBinary, *browserExtraArgs); err != nil {
glog.Errorf("Error while running benchmark: %s", err)
return
}
}
// Convert output to dashboard JSON v1 in order to upload to chromeperf.
// More information is in http://www.chromium.org/developers/speed-infra/performance-dashboard/sending-data-to-the-performance-dashboard
client := skutil.NewTimeoutClient()
outputFileInfos, err := ioutil.ReadDir(localOutputDir)
if err != nil {
glog.Errorf("Unable to read %s: %s", localOutputDir, err)
return
}
for _, fileInfo := range outputFileInfos {
if !fileInfo.IsDir() {
continue
}
resultsFile := filepath.Join(localOutputDir, fileInfo.Name(), "results-chart.json")
if err := uploadResultsToPerfDashboard(resultsFile, client); err != nil {
glog.Errorf("Could not upload to perf dashboard: %s", err)
continue
}
}
}
开发者ID:1394,项目名称:skia-buildbot,代码行数:77,代码来源:main.go
注:本文中的go/skia/org/infra/go/util.NewTimeoutClient函数示例由纯净天空整理自Github/MSDocs等源码及文档管理平台,相关代码片段筛选自各路编程大神贡献的开源项目,源码版权归原作者所有,传播和使用请参考对应项目的License;未经允许,请勿转载。 |
请发表评论