本文整理汇总了Golang中google/golang.org/grpc/credentials.NewClientTLSFromFile函数的典型用法代码示例。如果您正苦于以下问题:Golang NewClientTLSFromFile函数的具体用法?Golang NewClientTLSFromFile怎么用?Golang NewClientTLSFromFile使用的例子?那么恭喜您, 这里精选的函数代码示例或许可以为您提供帮助。
在下文中一共展示了NewClientTLSFromFile函数的20个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于我们的系统推荐出更棒的Golang代码示例。
示例1: setUp
func setUp(t *testing.T, useTLS bool, port int, maxStreams uint32, ht hType) (*server, ClientTransport) {
server := &server{readyChan: make(chan bool)}
go server.start(useTLS, port, maxStreams, ht)
server.wait(t, 2*time.Second)
addr := "localhost:" + server.port
var (
ct ClientTransport
connErr error
)
if useTLS {
creds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", "x.test.youtube.com")
if err != nil {
t.Fatalf("Failed to create credentials %v", err)
}
dopts := ConnectOptions{
AuthOptions: []credentials.Credentials{creds},
}
ct, connErr = NewClientTransport(addr, &dopts)
} else {
ct, connErr = NewClientTransport(addr, &ConnectOptions{})
}
if connErr != nil {
t.Fatalf("failed to create transport: %v", connErr)
}
return server, ct
}
开发者ID:progrium,项目名称:notary,代码行数:26,代码来源:transport_test.go
示例2: newEventsClientConnectionWithAddress
//newEventsClientConnectionWithAddress Returns a new grpc.ClientConn to the configured local PEER.
func newEventsClientConnectionWithAddress(peerAddress string) (*grpc.ClientConn, error) {
var opts []grpc.DialOption
if peer.TLSEnabled() {
var sn string
if viper.GetString("peer.tls.serverhostoverride") != "" {
sn = viper.GetString("peer.tls.serverhostoverride")
}
var creds credentials.TransportAuthenticator
if viper.GetString("peer.tls.cert.file") != "" {
var err error
creds, err = credentials.NewClientTLSFromFile(viper.GetString("peer.tls.cert.file"), sn)
if err != nil {
grpclog.Fatalf("Failed to create TLS credentials %v", err)
}
} else {
creds = credentials.NewClientTLSFromCert(nil, sn)
}
opts = append(opts, grpc.WithTransportCredentials(creds))
}
opts = append(opts, grpc.WithTimeout(defaultTimeout))
opts = append(opts, grpc.WithBlock())
opts = append(opts, grpc.WithInsecure())
return grpc.Dial(peerAddress, opts...)
}
开发者ID:magooster,项目名称:obc-peer,代码行数:26,代码来源:consumer.go
示例3: newPeerClientConnection
func newPeerClientConnection() (*grpc.ClientConn, error) {
var opts []grpc.DialOption
if viper.GetBool("peer.tls.enabled") {
var sn string
if viper.GetString("peer.tls.serverhostoverride") != "" {
sn = viper.GetString("peer.tls.serverhostoverride")
}
var creds credentials.TransportAuthenticator
if viper.GetString("peer.tls.cert.file") != "" {
var err error
creds, err = credentials.NewClientTLSFromFile(viper.GetString("peer.tls.cert.file"), sn)
if err != nil {
grpclog.Fatalf("Failed to create TLS credentials %v", err)
}
} else {
creds = credentials.NewClientTLSFromCert(nil, sn)
}
opts = append(opts, grpc.WithTransportCredentials(creds))
}
opts = append(opts, grpc.WithTimeout(1*time.Second))
opts = append(opts, grpc.WithBlock())
opts = append(opts, grpc.WithInsecure())
conn, err := grpc.Dial(getPeerAddress(), opts...)
if err != nil {
return nil, err
}
return conn, err
}
开发者ID:magooster,项目名称:obc-peer,代码行数:28,代码来源:chaincode.go
示例4: NewRoloClient
func NewRoloClient(idToken jose.JWT, tls bool, addr, serverHostOverride, trustedCaFile string) (*RoloClient, error) {
var opts []grpc.DialOption
creds := grpcoidc.NewOIDCAccess(&idToken)
opts = append(opts, grpc.WithPerRPCCredentials(creds))
if tls {
var sn string
if serverHostOverride != "" {
sn = serverHostOverride
}
var creds credentials.TransportAuthenticator
if trustedCaFile != "" {
var err error
creds, err = credentials.NewClientTLSFromFile(trustedCaFile, sn)
if err != nil {
return nil, err
}
}
opts = append(opts, grpc.WithTransportCredentials(creds))
}
conn, err := grpc.Dial(addr, opts...)
if err != nil {
return nil, err
}
return &RoloClient{
grpcClient: pb.NewRoloClient(conn),
}, nil
}
开发者ID:polvi,项目名称:rolo,代码行数:27,代码来源:client.go
示例5: main
func main() {
cmd := kingpin.Parse()
logger := logging.DefaultLogger
var creds credentials.TransportCredentials
var err error
if *caCert != "" {
creds, err = credentials.NewClientTLSFromFile(*caCert, "")
if err != nil {
logger.Fatal(err)
}
}
client, err := client.New(*address, creds, logger)
if err != nil {
logger.Fatalf("Could not set up grpc client: %s", err)
}
switch cmd {
case cmdScheduleText:
schedule(client, logger)
case cmdWatchStatusText:
watchStatus(client, logger)
}
}
开发者ID:petertseng,项目名称:p2,代码行数:26,代码来源:podstore.go
示例6: serve
func serve(opts []grpc.ServerOption) {
grpcServer := grpc.NewServer(opts...)
pb.RegisterYourServiceServer(grpcServer, newServer())
ctx := context.Background()
name := fmt.Sprintf("localhost:%d", *port)
dcreds, err := credentials.NewClientTLSFromFile(*certFile, name)
if err != nil {
fmt.Printf("serve: %v\n", err)
return
}
dopts := []grpc.DialOption{grpc.WithTransportCredentials(dcreds)}
mux := runtime.NewServeMux()
err = pb.RegisterYourServiceHandlerFromEndpoint(ctx, mux, name, dopts)
if err != nil {
fmt.Printf("serve: %v\n", err)
return
}
err = http.ListenAndServeTLS(fmt.Sprintf(":%d", *port), *certFile, *keyFile, grpcHandlerFunc(grpcServer, mux))
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
return
}
开发者ID:philips,项目名称:hacks,代码行数:27,代码来源:main.go
示例7: NewCaClient
func NewCaClient(addr string, tls bool, serverHostOverride string, trustedCaFile string) (*CaClient, error) {
var opts []grpc.DialOption
if tls {
var sn string
if serverHostOverride != "" {
sn = serverHostOverride
}
var creds credentials.TransportAuthenticator
if trustedCaFile != "" {
var err error
creds, err = credentials.NewClientTLSFromFile(trustedCaFile, sn)
if err != nil {
return nil, err
}
}
opts = append(opts, grpc.WithTransportCredentials(creds))
}
conn, err := grpc.Dial(addr, opts...)
if err != nil {
return nil, err
}
defer conn.Close()
return &CaClient{
grpcClient: pb.NewCaClient(conn),
}, nil
}
开发者ID:polvi,项目名称:cad,代码行数:26,代码来源:client.go
示例8: getDevopsClient
func getDevopsClient(peerAddress string) (obc.DevopsClient, error) {
var opts []grpc.DialOption
if viper.GetBool("pki.validity-period.tls.enabled") {
var sn string
if viper.GetString("pki.validity-period.tls.server-host-override") != "" {
sn = viper.GetString("pki.validity-period.tls.server-host-override")
}
var creds credentials.TransportAuthenticator
if viper.GetString("pki.validity-period.tls.cert.file") != "" {
var err error
creds, err = credentials.NewClientTLSFromFile(viper.GetString("pki.validity-period.tls.cert.file"), sn)
if err != nil {
grpclog.Fatalf("Failed to create TLS credentials %v", err)
}
} else {
creds = credentials.NewClientTLSFromCert(nil, sn)
}
opts = append(opts, grpc.WithTransportCredentials(creds))
}
opts = append(opts, grpc.WithTimeout(systemChaincodeTimeout))
opts = append(opts, grpc.WithBlock())
opts = append(opts, grpc.WithInsecure())
conn, err := grpc.Dial(peerAddress, opts...)
if err != nil {
return nil, fmt.Errorf("Error trying to connect to local peer: %s", err)
}
devopsClient := obc.NewDevopsClient(conn)
return devopsClient, nil
}
开发者ID:tenc,项目名称:obc-peer-pre-public,代码行数:31,代码来源:validity_period.go
示例9: GRPCClient
func GRPCClient(addr, token, caFile string) (*RemoteU2FClient, error) {
var err error
var tCreds credentials.TransportCredentials
if caFile == "" {
tCreds = credentials.NewClientTLSFromCert(nil, "")
} else {
tCreds, err = credentials.NewClientTLSFromFile(caFile, "")
if err != nil {
return nil, fmt.Errorf("error reading CA file: %s", err)
}
}
t := oauth2.Token{
AccessToken: token,
TokenType: "Bearer",
}
rpcCreds := oauth.NewOauthAccess(&t)
conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(tCreds),
grpc.WithPerRPCCredentials(rpcCreds),
grpc.WithBlock(),
grpc.WithTimeout(30*time.Second))
if err != nil {
return nil, fmt.Errorf("error connecting to server: %s", err)
}
c := pb.NewRemoteU2FClient(conn)
return &RemoteU2FClient{c}, nil
}
开发者ID:albertito,项目名称:remoteu2f,代码行数:29,代码来源:grpc.go
示例10: main
func main() {
fmt.Println("fe svc running...")
log.Println("authenticating...")
authServiceRequestURL := fmt.Sprintf("http://%s/authenticate", authServerAddress)
resp, err := http.PostForm(authServiceRequestURL,
url.Values{"username": {"mark"}, "password": {"secret"}})
if err != nil {
log.Fatal(err)
return
}
defer resp.Body.Close()
token, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
return
}
log.Println("calling service 1...")
ctxMarshaller := auth.NewContextMarshaller(context.Background())
ctx := ctxMarshaller.MarshalTrustedString(string(token))
if err != nil {
log.Fatal(err)
return
}
creds, err := credentials.NewClientTLSFromFile("../keys/ca.pem", svc1servername)
if err != nil {
log.Fatalf("error loading creds: %v", err)
}
conn, err := grpc.Dial(svc1address, grpc.WithTransportCredentials(creds))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
// Contact the server and print out its response.
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: "world"})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.Message)
}
开发者ID:mdevilliers,项目名称:golang-bestiary,代码行数:54,代码来源:main.go
示例11: demoConn
func demoConn(gwid string) {
var opts []grpc.DialOption
var err error
if demoTLS {
var sn string
if demoOver != "" {
sn = demoOver
}
var creds credentials.TransportCredentials
if demoFile != "" {
creds, err = credentials.NewClientTLSFromFile(demoFile, sn)
if err != nil {
grpclog.Fatalf("Failed to create TLS credentials %v", err)
}
} else {
creds = credentials.NewClientTLSFromCert(nil, sn)
}
opts = append(opts, grpc.WithTransportCredentials(creds))
} else {
opts = append(opts, grpc.WithInsecure())
}
conn, err := grpc.Dial(demoAddr, opts...)
if err != nil {
grpclog.Fatalf("fail to dial: %v", err)
}
//defer demoCliConn.Close()
demoClient = demo.NewRPCClient(conn)
go func() {
for {
ctx, _ := context.WithTimeout(context.Background(), 1*time.Second)
r, err := demoClient.GWSRPCCHECK(ctx, &demo.GWSRPCCHECKSEND{GWSRPCID: gwid})
if err != nil {
//grpclog.Printf("demoClient %v: ", err)
time.Sleep(100 * time.Millisecond)
continue
}
if r.GWSRPCID != demoID {
demoID = r.GWSRPCID
h.reinitall <- "demo"
}
time.Sleep(100 * time.Millisecond)
}
}()
}
开发者ID:vanishs,项目名称:gwsrpc,代码行数:54,代码来源:gengateway.go
示例12: TestTLSDialTimeout
func TestTLSDialTimeout(t *testing.T) {
creds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", "x.test.youtube.com")
if err != nil {
t.Fatalf("Failed to create credentials %v", err)
}
conn, err := grpc.Dial("Non-Existent.Server:80", grpc.WithTransportCredentials(creds), grpc.WithTimeout(time.Millisecond), grpc.WithBlock())
if err == nil {
conn.Close()
}
if err != grpc.ErrClientConnTimeout {
t.Fatalf("grpc.Dial(_, _) = %v, %v, want %v", conn, err, grpc.ErrClientConnTimeout)
}
}
开发者ID:ELMERzark,项目名称:grpc-go,代码行数:13,代码来源:end2end_test.go
示例13: TestCredentialsMisuse
func TestCredentialsMisuse(t *testing.T) {
tlsCreds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", "x.test.youtube.com")
if err != nil {
t.Fatalf("Failed to create authenticator %v", err)
}
// Two conflicting credential configurations
if _, err := Dial("Non-Existent.Server:80", WithTransportCredentials(tlsCreds), WithBlock(), WithInsecure()); err != errCredentialsConflict {
t.Fatalf("Dial(_, _) = _, %v, want _, %v", err, errCredentialsConflict)
}
// security info on insecure connection
if _, err := Dial("Non-Existent.Server:80", WithPerRPCCredentials(securePerRPCCredentials{}), WithBlock(), WithInsecure()); err != errTransportCredentialsMissing {
t.Fatalf("Dial(_, _) = _, %v, want _, %v", err, errTransportCredentialsMissing)
}
}
开发者ID:tamird,项目名称:grpc-go,代码行数:14,代码来源:clientconn_test.go
示例14: TestCredentialsMisuse
func TestCredentialsMisuse(t *testing.T) {
creds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", "x.test.youtube.com")
if err != nil {
t.Fatalf("Failed to create credentials %v", err)
}
// Two conflicting credential configurations
if _, err := grpc.Dial("Non-Existent.Server:80", grpc.WithTransportCredentials(creds), grpc.WithTimeout(time.Millisecond), grpc.WithBlock(), grpc.WithInsecure()); err != grpc.ErrCredentialsMisuse {
t.Fatalf("grpc.Dial(_, _) = _, %v, want _, %v", err, grpc.ErrCredentialsMisuse)
}
// security info on insecure connection
if _, err := grpc.Dial("Non-Existent.Server:80", grpc.WithPerRPCCredentials(creds), grpc.WithTimeout(time.Millisecond), grpc.WithBlock(), grpc.WithInsecure()); err != grpc.ErrCredentialsMisuse {
t.Fatalf("grpc.Dial(_, _) = _, %v, want _, %v", err, grpc.ErrCredentialsMisuse)
}
}
开发者ID:johnmccawley,项目名称:origin,代码行数:14,代码来源:end2end_test.go
示例15: setUp
func setUp(hs *health.HealthServer, maxStream uint32, ua string, e env) (s *grpc.Server, cc *grpc.ClientConn) {
sopts := []grpc.ServerOption{grpc.MaxConcurrentStreams(maxStream)}
la := ":0"
switch e.network {
case "unix":
la = "/tmp/testsock" + fmt.Sprintf("%d", time.Now())
syscall.Unlink(la)
}
lis, err := net.Listen(e.network, la)
if err != nil {
grpclog.Fatalf("Failed to listen: %v", err)
}
if e.security == "tls" {
creds, err := credentials.NewServerTLSFromFile(tlsDir+"server1.pem", tlsDir+"server1.key")
if err != nil {
grpclog.Fatalf("Failed to generate credentials %v", err)
}
sopts = append(sopts, grpc.Creds(creds))
}
s = grpc.NewServer(sopts...)
if hs != nil {
healthpb.RegisterHealthCheckServer(s, hs)
}
testpb.RegisterTestServiceServer(s, &testServer{})
go s.Serve(lis)
addr := la
switch e.network {
case "unix":
default:
_, port, err := net.SplitHostPort(lis.Addr().String())
if err != nil {
grpclog.Fatalf("Failed to parse listener address: %v", err)
}
addr = "localhost:" + port
}
if e.security == "tls" {
creds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", "x.test.youtube.com")
if err != nil {
grpclog.Fatalf("Failed to create credentials %v", err)
}
cc, err = grpc.Dial(addr, grpc.WithTransportCredentials(creds), grpc.WithDialer(e.dialer), grpc.WithUserAgent(ua))
} else {
cc, err = grpc.Dial(addr, grpc.WithDialer(e.dialer), grpc.WithUserAgent(ua))
}
if err != nil {
grpclog.Fatalf("Dial(%q) = %v", addr, err)
}
return
}
开发者ID:ELMERzark,项目名称:grpc-go,代码行数:49,代码来源:end2end_test.go
示例16: TestTLSServerNameOverwrite
func TestTLSServerNameOverwrite(t *testing.T) {
overwriteServerName := "over.write.server.name"
creds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", overwriteServerName)
if err != nil {
t.Fatalf("Failed to create credentials %v", err)
}
conn, err := Dial("Non-Existent.Server:80", WithTransportCredentials(creds))
if err != nil {
t.Fatalf("Dial(_, _) = _, %v, want _, <nil>", err)
}
conn.Close()
if conn.authority != overwriteServerName {
t.Fatalf("%v.authority = %v, want %v", conn, conn.authority, overwriteServerName)
}
}
开发者ID:tamird,项目名称:grpc-go,代码行数:15,代码来源:clientconn_test.go
示例17: main
func main() {
flag.Parse()
var opts []grpc.DialOption
// 如何使用: TLS呢?
if *tls {
var sn string
if *serverHostOverride != "" {
sn = *serverHostOverride
}
var creds credentials.TransportAuthenticator
if *caFile != "" {
var err error
creds, err = credentials.NewClientTLSFromFile(*caFile, sn)
if err != nil {
grpclog.Fatalf("Failed to create TLS credentials %v", err)
}
} else {
creds = credentials.NewClientTLSFromCert(nil, sn)
}
opts = append(opts, grpc.WithTransportCredentials(creds))
} else {
opts = append(opts, grpc.WithInsecure())
}
// 2. 连接服务器
conn, err := grpc.Dial(*serverAddr, opts...)
if err != nil {
grpclog.Fatalf("fail to dial: %v", err)
}
defer conn.Close()
client := pb.NewRouteGuideClient(conn)
// Looking for a valid feature
printFeature(client, &pb.Point{409146138, -746188906})
// Feature missing.
printFeature(client, &pb.Point{0, 0})
// Looking for features between 40, -75 and 42, -73.
printFeatures(client, &pb.Rectangle{&pb.Point{400000000, -750000000}, &pb.Point{420000000, -730000000}})
// RecordRoute
runRecordRoute(client)
// RouteChat
runRouteChat(client)
}
开发者ID:wfxiang08,项目名称:grpc-go,代码行数:48,代码来源:client.go
示例18: clientSetUp
func clientSetUp(t *testing.T, addr string, cg grpc.CompressorGenerator, dg grpc.DecompressorGenerator, ua string, e env) (cc *grpc.ClientConn) {
var derr error
if e.security == "tls" {
creds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", "x.test.youtube.com")
if err != nil {
t.Fatalf("Failed to create credentials %v", err)
}
cc, derr = grpc.Dial(addr, grpc.WithTransportCredentials(creds), grpc.WithDialer(e.dialer), grpc.WithUserAgent(ua), grpc.WithCompressor(cg), grpc.WithDecompressor(dg))
} else {
cc, derr = grpc.Dial(addr, grpc.WithDialer(e.dialer), grpc.WithInsecure(), grpc.WithUserAgent(ua), grpc.WithCompressor(cg), grpc.WithDecompressor(dg))
}
if derr != nil {
t.Fatalf("Dial(%q) = %v", addr, derr)
}
return
}
开发者ID:slafgod000,项目名称:grpc-go,代码行数:16,代码来源:end2end_test.go
示例19: setUp
func setUp(maxStream uint32, e env) (s *grpc.Server, cc *grpc.ClientConn) {
s = grpc.NewServer(grpc.MaxConcurrentStreams(maxStream))
la := ":0"
switch e.network {
case "unix":
la = "/tmp/testsock" + fmt.Sprintf("%p", s)
syscall.Unlink(la)
}
lis, err := net.Listen(e.network, la)
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
testpb.RegisterTestServiceServer(s, &testServer{})
if e.security == "tls" {
creds, err := credentials.NewServerTLSFromFile(tlsDir+"server1.pem", tlsDir+"server1.key")
if err != nil {
log.Fatalf("Failed to generate credentials %v", err)
}
go s.Serve(creds.NewListener(lis))
} else {
go s.Serve(lis)
}
addr := la
switch e.network {
case "unix":
default:
_, port, err := net.SplitHostPort(lis.Addr().String())
if err != nil {
log.Fatalf("Failed to parse listener address: %v", err)
}
addr = "localhost:" + port
}
if e.security == "tls" {
creds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", "x.test.youtube.com")
if err != nil {
log.Fatalf("Failed to create credentials %v", err)
}
cc, err = grpc.Dial(addr, grpc.WithTransportCredentials(creds), grpc.WithDialer(e.dialer))
} else {
cc, err = grpc.Dial(addr, grpc.WithDialer(e.dialer))
}
if err != nil {
log.Fatalf("Dial(%q) = %v", addr, err)
}
return
}
开发者ID:progrium,项目名称:notary,代码行数:46,代码来源:end2end_test.go
示例20: createConns
// createConns creates connections according to given config.
// It returns the connections and corresponding function to close them.
// It returns non-nil error if there is anything wrong.
func createConns(config *testpb.ClientConfig) ([]*grpc.ClientConn, func(), error) {
var opts []grpc.DialOption
// Sanity check for client type.
switch config.ClientType {
case testpb.ClientType_SYNC_CLIENT:
case testpb.ClientType_ASYNC_CLIENT:
default:
return nil, nil, grpc.Errorf(codes.InvalidArgument, "unknow client type: %v", config.ClientType)
}
// Check and set security options.
if config.SecurityParams != nil {
creds, err := credentials.NewClientTLSFromFile(abs(caFile), config.SecurityParams.ServerHostOverride)
if err != nil {
return nil, nil, grpc.Errorf(codes.InvalidArgument, "failed to create TLS credentials %v", err)
}
opts = append(opts, grpc.WithTransportCredentials(creds))
} else {
opts = append(opts, grpc.WithInsecure())
}
// Use byteBufCodec if it is required.
if config.PayloadConfig != nil {
switch config.PayloadConfig.Payload.(type) {
case *testpb.PayloadConfig_BytebufParams:
opts = append(opts, grpc.WithCodec(byteBufCodec{}))
case *testpb.PayloadConfig_SimpleParams:
default:
return nil, nil, grpc.Errorf(codes.InvalidArgument, "unknow payload config: %v", config.PayloadConfig)
}
}
// Create connections.
connCount := int(config.ClientChannels)
conns := make([]*grpc.ClientConn, connCount, connCount)
for connIndex := 0; connIndex < connCount; connIndex++ {
conns[connIndex] = benchmark.NewClientConn(config.ServerTargets[connIndex%len(config.ServerTargets)], opts...)
}
return conns, func() {
for _, conn := range conns {
conn.Close()
}
}, nil
}
开发者ID:ruinanchen,项目名称:grpc-go,代码行数:49,代码来源:benchmark_client.go
注:本文中的google/golang.org/grpc/credentials.NewClientTLSFromFile函数示例由纯净天空整理自Github/MSDocs等源码及文档管理平台,相关代码片段筛选自各路编程大神贡献的开源项目,源码版权归原作者所有,传播和使用请参考对应项目的License;未经允许,请勿转载。 |
请发表评论