func (req *RtmpRequest) Parse(cc, args *Amf0Object, logger core.Logger) (err error) { if v, ok := cc.GetString("tcUrl"); ok { req.TcUrl = string(v) } else { logger.Error("invalid request, must specifies the tcUrl.") return RtmpTcUrlNotString } if v, ok := cc.GetString("pageUrl"); ok { req.PageUrl = string(v) } if v, ok := cc.GetString("swfUrl"); ok { req.SwfUrl = string(v) } if v, ok := cc.GetNumber("objectEncoding"); ok { req.ObjectEncoding = int(float64(v)) } if args != nil { req.Args = args logger.Info("copy edge traverse to origin auth args.") } logger.Info("get connect app message params success.") req.Schema, req.Host, req.Vhost, req.App, req.Port, req.Param, err = DiscoveryTcUrl(req.TcUrl, logger) logger.Info("tcUrl=%v parsed", req.TcUrl) return }
func FindSource(req *protocol.RtmpRequest, logger core.Logger) (source *RtmpSource, err error) { url := req.StreamUrl() if _, ok := sources[url]; !ok { source = NewRtmpSource(req, logger) if err = source.Initialize(); err != nil { return } sources[url] = source logger.Info("create new source for url=%s, vhost=%s", url, req.Vhost) } // we always update the request of resource, // for origin auth is on, the token in request maybe invalid, // and we only need to update the token of request, it's simple. source = sources[url] source.Req.UpdateAuth(req) return }
func (req *RtmpRequest) Validate(logger core.Logger) (err error) { if req.Schema == "" { logger.Error("request schema is empty") return RtmpRequestSchemaEmpty } if req.Vhost == "" { logger.Error("request vhost is empty") return RtmpRequestVhostEmpty } if req.Port <= 0 { logger.Error("request port is not positive") return RtmpRequestPortEmpty } if req.App == "" { logger.Error("request app is empty") return RtmpRequestAppEmpty } logger.Info("request validate ok") return }
func (pkt *RtmpConnectAppPacket) Decode(buffer *bytes.Buffer, logger core.Logger) (err error) { if err = pkt.rtmpCommonCallPacket.Decode(buffer, logger); err != nil { return } // some client donot send id=1.0, so we only warn user if not match. if pkt.TransactionId != 1.0 { logger.Warn("connect should be 1.0, actual is %v", pkt.TransactionId) } if err = pkt.CommandObject.Decode(buffer); err != nil { logger.Error("amf0 decode connect command_object failed.") return } if buffer.Len() > 0 { // see: https://github.com/winlinvip/simple-rtmp-server/issues/186 // the args maybe any amf0, for instance, a string. we should drop if not object. var any Amf0Any if any, err = DecodeAmf0Any(buffer); err != nil { logger.Error("amf0 decode connect args failed") return } // drop when not an AMF0 object. if any.(*Amf0Object) == nil { logger.Warn("drop the args, see: '4.1.1. connect'") } else { pkt.Arguments = any.(*Amf0Object) } } logger.Info("amf0 decode connect packet success") return }
func DiscoveryTcUrl(tcUrl string, logger core.Logger) (schema, host, vhost, app string, port int, param string, err error) { rawurl := tcUrl // parse the ...vhost... to standard query &vhost= for strings.Index(rawurl, "...") >= 0 { rawurl = strings.Replace(rawurl, "...", "&", 1) rawurl = strings.Replace(rawurl, "...", "=", 1) } // use url module to parse. var uri *url.URL if uri, err = url.Parse(tcUrl); err != nil { logger.Error("parse tcUrl=%v failed", tcUrl) return } schema = uri.Scheme host = uri.Host app = strings.Trim(uri.Path, "/") param = uri.RawQuery port = core.SRS_CONSTS_RTMP_DEFAULT_PORT if pos := strings.Index(host, ":"); pos >= 0 { if port, err = strconv.Atoi(host[pos+1:]); err != nil { logger.Error("parse port from host=%v failed", host) return } host = host[0:pos] logger.Info("discovery host=%v, port=%v", host, port) } vhost = host query := uri.Query() if query.Get("vhost") != "" { vhost = query.Get("vhost") } logger.Info("tcUrl parsed to schema=%v, host=%v, port=%v, vhost=%v, app=%v, param=%v", schema, host, port, vhost, app, param) return }
func DiscoveryPacket(msg *RtmpMessage, logger core.Logger) (b []byte, pkt RtmpPacket, err error) { header := msg.Header b = msg.Payload if msg == nil || len(msg.Payload) == 0 { logger.Info("ignore empty msg") return } // decode specified packet type if header.IsAmf0Command() || header.IsAmf3Command() || header.IsAmf0Data() || header.IsAmf3Data() { logger.Info("start to decode AMF0/AMF3 command message.") // skip 1bytes to decode the amf3 command. if header.IsAmf3Command() { b = b[1:] logger.Info("skip 1bytes to decode AMF3 command") } // amf0 command message. // need to read the command name. var command Amf0String if command, err = DecodeAmf0String(bytes.NewBuffer(b)); err != nil { logger.Error("decode AMF0/AMF3 command name failed.") return } logger.Info("AMF0/AMF3 command message, command_name=%v", command) // result/error packet if command == RTMP_AMF0_COMMAND_RESULT || command == RTMP_AMF0_COMMAND_ERROR { } // decode command object. switch command { case RTMP_AMF0_COMMAND_CONNECT: logger.Info("decode the AMF0/AMF3 command(connect vhost/app message).") pkt = NewRtmpConnectAppPacket() case RTMP_AMF0_COMMAND_CREATE_STREAM: logger.Info("decode the AMF0/AMF3 command(createStream message).") pkt = NewRtmpCreateStreamPacket() case RTMP_AMF0_COMMAND_PLAY: logger.Info("decode the AMF0/AMF3 command(paly message).") pkt = NewRtmpPlayPacket() case RTMP_AMF0_COMMAND_RELEASE_STREAM: logger.Info("decode the AMF0/AMF3 command(FMLE releaseStream message).") pkt = NewRtmpReleaseStreamPacket() case RTMP_AMF0_COMMAND_FC_PUBLISH: logger.Info("decode the AMF0/AMF3 command(FMLE FCPublish message).") pkt = NewRtmpFcPublishPacket() case RTMP_AMF0_COMMAND_UNPUBLISH: logger.Info("decode the AMF0/AMF3 command(FMLE FCUnpublish message).") pkt = NewRtmpFcUnPublishPacket() case RTMP_AMF0_COMMAND_PUBLISH: logger.Info("decode the AMF0/AMF3 command(publish message).") pkt = NewRtmpPublishPacket() default: if header.IsAmf0Command() || header.IsAmf3Command() { logger.Info("decode the AMF0/AMF3 call message.") pkt = NewRtmpCallPacket() } else { logger.Info("drop the AMF0/AMF3 command message, command_name=%v", command) } } } else if header.IsUserControlMessage() { logger.Info("start to decode user control message.") pkt = NewRtmpUserControlPacket() } else if header.IsWindowAckledgementSize() { logger.Info("start to decode set ack window size message.") pkt = NewRtmpSetWindowAckSizePacket(0) } else if header.IsSetChunkSize() { logger.Info("start to decode set chunk size message.") pkt = NewRtmpSetChunkSizePacket() } else { } return }