github.com/ssetin/penguincast@v0.2.0/src/server/protocol.go (about)

     1  // Package iceserver - icecast streaming server
     2  package iceserver
     3  
     4  /*
     5  	TODO:
     6  */
     7  
     8  import (
     9  	"bufio"
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"log"
    14  	"net"
    15  	"net/http"
    16  	"strconv"
    17  	"strings"
    18  	"sync/atomic"
    19  	"time"
    20  )
    21  
    22  //223.33.152.54 - - [27/Feb/2012:13:37:21 +0300] "GET /gop_aac HTTP/1.1" 200 75638 "-" "WMPlayer/10.0.0.364 guid/3300AD50-2C39-46C0-AE0A-AC7B8159E203" 400
    23  func (i *IceServer) closeMount(idx int, issource bool, bytessended *int, start time.Time, r *http.Request) {
    24  	if issource {
    25  		i.decSources()
    26  		i.Props.Mounts[idx].Clear()
    27  	} else {
    28  		i.Props.Mounts[idx].decListeners()
    29  		i.decListeners()
    30  	}
    31  	t := time.Now()
    32  	elapsed := t.Sub(start)
    33  	i.printAccess("%s - - [%s] \"%s\" %s %d \"%s\" \"%s\" %d\r\n", i.getHost(r.RemoteAddr), start.Format(time.RFC1123Z), r.Method+" "+r.RequestURI+" "+r.Proto, "200", *bytessended, r.Referer(), r.UserAgent(), int(elapsed.Seconds()))
    34  }
    35  
    36  func (i *IceServer) getHost(addr string) string {
    37  	idx := strings.Index(addr, ":")
    38  	if idx == -1 {
    39  		return addr
    40  	}
    41  	return addr[:idx]
    42  }
    43  
    44  func (i *IceServer) logHeaders(w http.ResponseWriter, r *http.Request) {
    45  	request := r.Method + " " + r.RequestURI + " " + r.Proto + "\n"
    46  	for name, headers := range r.Header {
    47  		name = strings.ToLower(name)
    48  		for _, h := range headers {
    49  			request += fmt.Sprintf("%v: %v\n", name, h)
    50  		}
    51  	}
    52  	i.printError(4, "\n"+request)
    53  }
    54  
    55  /*
    56  	openMount
    57      Decide what to do, according to HTTP method
    58  */
    59  func (i *IceServer) openMount(idx int, w http.ResponseWriter, r *http.Request) {
    60  	if r.Method == "SOURCE" || r.Method == "PUT" {
    61  		if !i.checkSources() {
    62  			i.printError(1, "Number of sources exceeded")
    63  			http.Error(w, "Number of sources exceeded", 403)
    64  			return
    65  		}
    66  		i.writeMount(idx, w, r)
    67  	} else {
    68  		if !i.checkListeners() {
    69  			i.printError(1, "Number of listeners exceeded")
    70  			http.Error(w, "Number of listeners exceeded", 403)
    71  			return
    72  		}
    73  		var im = false
    74  		if r.Header.Get("icy-metadata") == "1" {
    75  			im = true
    76  		}
    77  		i.readMount(idx, im, w, r)
    78  	}
    79  }
    80  
    81  func (i *IceServer) closeAndUnlock(pack *BufElement, err error) {
    82  	if te, ok := err.(net.Error); ok && te.Timeout() {
    83  		log.Println("Write timeout")
    84  		i.printError(1, "Write timeout")
    85  	} else {
    86  		i.printError(1, err.Error())
    87  	}
    88  	pack.UnLock()
    89  }
    90  
    91  /*
    92  	readMount
    93  	Send stream from requested mount to client
    94  */
    95  func (i *IceServer) readMount(idx int, icymeta bool, w http.ResponseWriter, r *http.Request) {
    96  	var mount *Mount
    97  	var meta []byte
    98  	var err error
    99  	var pack, nextpack *BufElement
   100  
   101  	bytessended := 0
   102  	writed := 0
   103  	idle := 0
   104  	idleTimeOut := i.Props.Limits.EmptyBufferIdleTimeOut * 1000
   105  	writeTimeOut := time.Second * time.Duration(i.Props.Limits.WriteTimeOut)
   106  	offset := 0
   107  	nometabytes := 0
   108  	partwrited := 0
   109  	nmtmp := 0
   110  	delta := 0
   111  	beginIteration := time.Now()
   112  	metalen := 0
   113  	n := 0
   114  
   115  	hj, ok := w.(http.Hijacker)
   116  	if !ok {
   117  		i.printError(1, "webserver doesn't support hijacking")
   118  		http.Error(w, "webserver doesn't support hijacking", http.StatusInternalServerError)
   119  		return
   120  	}
   121  
   122  	conn, bufrw, err := hj.Hijack()
   123  	if err != nil {
   124  		http.Error(w, err.Error(), http.StatusInternalServerError)
   125  		return
   126  	}
   127  	defer conn.Close()
   128  
   129  	start := time.Now()
   130  	mount = &i.Props.Mounts[idx]
   131  
   132  	i.printError(3, "readMount "+mount.Name)
   133  	defer i.closeMount(idx, false, &bytessended, start, r)
   134  
   135  	//try to maximize unused buffer pages from begining
   136  	pack = mount.buffer.Start(mount.BurstSize)
   137  
   138  	if pack == nil {
   139  		i.printError(1, "readMount Empty buffer")
   140  		return
   141  	}
   142  
   143  	//bufrw := bufio.NewWriterSize(conn, 1024*mount.BitRate/8)
   144  
   145  	mount.sayListenerHello(bufrw, icymeta)
   146  
   147  	i.incListeners()
   148  	mount.incListeners()
   149  
   150  	for {
   151  		beginIteration = time.Now()
   152  		conn.SetWriteDeadline(time.Now().Add(writeTimeOut))
   153  		//check, if server has to be stopped
   154  		if atomic.LoadInt32(&i.Started) == 0 {
   155  			break
   156  		}
   157  
   158  		n++
   159  		pack.Lock()
   160  		if icymeta {
   161  			meta, metalen = mount.getIcyMeta()
   162  
   163  			if nometabytes+pack.len+delta > mount.State.MetaInfo.MetaInt {
   164  				offset = mount.State.MetaInfo.MetaInt - nometabytes - delta
   165  
   166  				//log.Printf("*** write block with meta ***")
   167  				//log.Printf("   offset = %d - %d(nometabytes) - %d (delta) = %d", mount.State.MetaInfo.MetaInt, nometabytes, delta, offset)
   168  
   169  				if offset < 0 || offset >= pack.len {
   170  					i.printError(2, "Bad metainfo offset %d", offset)
   171  					log.Printf("!!! Bad metainfo offset %d ***", offset)
   172  					offset = 0
   173  				}
   174  
   175  				partwrited, err = bufrw.Write(pack.buffer[:offset])
   176  				if err != nil {
   177  					i.closeAndUnlock(pack, err)
   178  					break
   179  				}
   180  				writed += partwrited
   181  				partwrited, err = bufrw.Write(meta)
   182  				if err != nil {
   183  					i.closeAndUnlock(pack, err)
   184  					break
   185  				}
   186  				writed += partwrited
   187  				partwrited, err = bufrw.Write(pack.buffer[offset:])
   188  				if err != nil {
   189  					i.closeAndUnlock(pack, err)
   190  					break
   191  				}
   192  				writed += partwrited
   193  
   194  				delta = writed - offset - metalen
   195  				nometabytes = 0
   196  				nmtmp = 0
   197  
   198  				//log.Printf("   delta = %d(writed) - %d(offset) - %d(metalen) = %d", writed, offset, metalen, delta)
   199  			} else {
   200  				writed = 0
   201  				nmtmp, err = bufrw.Write(pack.buffer)
   202  				nometabytes += nmtmp
   203  			}
   204  		} else {
   205  			writed, err = bufrw.Write(pack.buffer)
   206  		}
   207  
   208  		if err != nil {
   209  			i.closeAndUnlock(pack, err)
   210  			break
   211  		}
   212  
   213  		bytessended += writed + nmtmp
   214  
   215  		// send burst data whithout waiting
   216  		if bytessended >= mount.BurstSize {
   217  			if time.Since(beginIteration) < time.Second {
   218  				time.Sleep(time.Second - time.Since(beginIteration))
   219  			}
   220  		}
   221  
   222  		nextpack = pack.Next()
   223  		for nextpack == nil {
   224  			time.Sleep(time.Millisecond * 250)
   225  			idle += 250
   226  			if idle >= idleTimeOut {
   227  				i.closeAndUnlock(pack, errors.New("Empty Buffer idle time is reached"))
   228  				break
   229  			}
   230  			nextpack = pack.Next()
   231  		}
   232  		idle = 0
   233  		pack.UnLock()
   234  
   235  		pack = nextpack
   236  	}
   237  }
   238  
   239  /*
   240  	writeMount
   241  	Authenticate SOURCE and write stream from it to appropriate mount buffer
   242  */
   243  func (i *IceServer) writeMount(idx int, w http.ResponseWriter, r *http.Request) {
   244  	var mount *Mount
   245  	mount = &i.Props.Mounts[idx]
   246  
   247  	if !mount.State.Started {
   248  		mount.mux.Lock()
   249  		err := mount.auth(w, r)
   250  		if err != nil {
   251  			mount.mux.Unlock()
   252  			i.printError(1, err.Error())
   253  			return
   254  		}
   255  		mount.writeICEHeaders(w, r)
   256  		mount.State.Started = true
   257  		mount.State.StartedTime = time.Now()
   258  		mount.mux.Unlock()
   259  	} else {
   260  		i.printError(1, "SOURCE already connected")
   261  		http.Error(w, "SOURCE already connected", 403)
   262  		return
   263  	}
   264  
   265  	bytessended := 0
   266  	idle := 0
   267  	readed := 0
   268  	var err error
   269  	start := time.Now()
   270  
   271  	i.printError(3, "writeMount "+mount.Name)
   272  	defer i.closeMount(idx, true, &bytessended, start, r)
   273  
   274  	hj, ok := w.(http.Hijacker)
   275  	if !ok {
   276  		i.printError(1, "webserver doesn't support hijacking")
   277  		http.Error(w, "webserver doesn't support hijacking", http.StatusInternalServerError)
   278  		return
   279  	}
   280  
   281  	conn, _, err := hj.Hijack()
   282  	if err != nil {
   283  		http.Error(w, err.Error(), http.StatusInternalServerError)
   284  		return
   285  	}
   286  	defer conn.Close()
   287  
   288  	bufrw := bufio.NewReaderSize(conn, 1024*mount.BitRate/8)
   289  
   290  	i.incSources()
   291  	// max bytes per second according to bitrate
   292  	buff := make([]byte, mount.BitRate*1024/8)
   293  
   294  	for {
   295  		//check, if server has to be stopped
   296  		if atomic.LoadInt32(&i.Started) == 0 {
   297  			break
   298  		}
   299  
   300  		readed, err = bufrw.Read(buff)
   301  		if err != nil {
   302  			if err == io.EOF {
   303  				idle++
   304  				if idle >= i.Props.Limits.SourceIdleTimeOut {
   305  					i.printError(1, "Source idle time is reached")
   306  					break
   307  				}
   308  			}
   309  			i.printError(3, err.Error())
   310  		} else {
   311  			idle = 0
   312  		}
   313  		// append to the buffer's queue based on actual readed bytes
   314  		mount.buffer.Append(buff, readed)
   315  		bytessended += readed
   316  		i.printError(4, "writeMount "+strconv.Itoa(readed)+"")
   317  
   318  		if mount.dumpFile != nil {
   319  			mount.dumpFile.Write(mount.buffer.Last().buffer)
   320  		}
   321  
   322  		time.Sleep(1000 * time.Millisecond)
   323  
   324  		//check if maxbuffersize reached and truncate it
   325  		mount.buffer.checkAndTruncate()
   326  	}
   327  }