package httpuimport ()// ClientInterface is the general interface provided to perform HTTP-over-UDP// requests.typeClientInterfaceinterface {// Do performs a request. The timeout is how long to wait for before returning // the responses that were received. An error is only returned for failing to // send the request. Failures in receipt simply do not add to the resulting // responses.Do( req *http.Request, timeout time.Duration, numSends int, ) ([]*http.Response, error)}// ClientInterfaceCtx is the equivalent of ClientInterface, except with methods// taking a context.Context parameter.typeClientInterfaceCtxinterface {// DoWithContext performs a request. If the input request has a // deadline, then that value will be used as the timeout for how long // to wait before returning the responses that were received. If the // request's context is canceled, this method will return immediately. // // If the request's context is never canceled, and does not have a // deadline, then this function WILL NEVER RETURN. You MUST set an // appropriate deadline on the context, or otherwise cancel it when you // want to finish an operation. // // An error is only returned for failing to send the request. Failures // in receipt simply do not add to the resulting responses.DoWithContext( req *http.Request, numSends int, ) ([]*http.Response, error)}// HTTPUClient is a client for dealing with HTTPU (HTTP over UDP). Its typical// function is for HTTPMU, and particularly SSDP.typeHTTPUClientstruct { connLock sync.Mutex// Protects use of conn. conn net.PacketConn}var _ ClientInterface = &HTTPUClient{}var _ ClientInterfaceCtx = &HTTPUClient{}// NewHTTPUClient creates a new HTTPUClient, opening up a new UDP socket for the// purpose.func () (*HTTPUClient, error) { , := net.ListenPacket("udp", ":0")if != nil {returnnil, }return &HTTPUClient{conn: }, nil}// NewHTTPUClientAddr creates a new HTTPUClient which will broadcast packets// from the specified address, opening up a new UDP socket for the purposefunc ( string) (*HTTPUClient, error) { := net.ParseIP()if == nil {returnnil, errors.New("Invalid listening address") } , := net.ListenPacket("udp", .String()+":0")if != nil {returnnil, }return &HTTPUClient{conn: }, nil}// Close shuts down the client. The client will no longer be useful following// this.func ( *HTTPUClient) () error { .connLock.Lock()defer .connLock.Unlock()return .conn.Close()}// Do implements ClientInterface.Do.//// Note that at present only one concurrent connection will happen per// HTTPUClient.func ( *HTTPUClient) ( *http.Request,time.Duration,int,) ([]*http.Response, error) { := .Context()if > 0 {varfunc() , = context.WithTimeout(, )defer () = .WithContext() }return .DoWithContext(, )}// DoWithContext implements ClientInterfaceCtx.DoWithContext.//// Make sure to read the documentation on the ClientInterfaceCtx interface// regarding cancellation!func ( *HTTPUClient) ( *http.Request,int,) ([]*http.Response, error) { .connLock.Lock()defer .connLock.Unlock()// Create the request. This is a subset of what http.Request.Write does // deliberately to avoid creating extra fields which may confuse some // devices.varbytes.Buffer := .Methodif == "" { = "GET" }if , := fmt.Fprintf(&, "%s %s HTTP/1.1\r\n", , .URL.RequestURI()); != nil {returnnil, }if := .Header.Write(&); != nil {returnnil, }if , := .Write([]byte{'\r', '\n'}); != nil {returnnil, } , := net.ResolveUDPAddr("udp", .Host)if != nil {returnnil, }// Handle context deadline/timeout := .Context() , := .Deadline()if {if = .conn.SetDeadline(); != nil {returnnil, } }// Handle context cancelation := make(chanstruct{})deferclose()gofunc() {select {case<-.Done():// if context is cancelled, stop any connections by setting time in the past. .conn.SetDeadline(time.Now().Add(-time.Second))case<-: } }()// Send request.for := 0; < ; ++ {if , := .conn.WriteTo(.Bytes(), ); != nil {returnnil, } elseif < len(.Bytes()) {returnnil, fmt.Errorf("httpu: wrote %d bytes rather than full %d in request", , len(.Bytes())) }time.Sleep(5 * time.Millisecond) }// Await responses until timeout.var []*http.Response := make([]byte, 2048)for {// 2048 bytes should be sufficient for most networks. , , := .conn.ReadFrom()if != nil {if , := .(net.Error); {if .Timeout() {break }if .Temporary() {// Sleep in case this is a persistent error to avoid pegging CPU until deadline.time.Sleep(10 * time.Millisecond)continue } }returnnil, }// Parse response. , := http.ReadResponse(bufio.NewReader(bytes.NewBuffer([:])), )if != nil {log.Printf("httpu: error while parsing response: %v", )continue }// Set the related local address used to discover the device.if , := .conn.LocalAddr().(*net.UDPAddr); { .Header.Add(LocalAddressHeader, .IP.String()) } = append(, ) }// Timeout reached - return discovered responses.return , nil}constLocalAddressHeader = "goupnp-local-address"
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.