/* Stream manufactures a `pkg/stream.Stream`, starts a worker pumping events out of decoding, and returns that. The 'outputCh' parameter must be a sendable channel. The "zero"-values of channel's content type will be created and used in the deserialization, then sent. The return values from `httpclient.RawReq` are probably a useful starting point for the 'res' parameter. Closing the returned `stream.Stream` shuts down the worker. */ func Stream(res *http.Response, outputCh interface{}) stream.Stream { stream := stream.New() var chanValue reflect.Value if v, ok := outputCh.(reflect.Value); ok { chanValue = v } else { chanValue = reflect.ValueOf(outputCh) } stopChanValue := reflect.ValueOf(stream.StopCh) msgType := chanValue.Type().Elem().Elem() go func() { done := make(chan struct{}) defer func() { chanValue.Close() close(done) }() go func() { select { case <-stream.StopCh: case <-done: } res.Body.Close() }() r := bufio.NewReader(res.Body) dec := sse.NewDecoder(r) for { msg := reflect.New(msgType) if err := dec.Decode(msg.Interface()); err != nil { if err != io.EOF { stream.Error = err } break } chosen, _, _ := reflect.Select([]reflect.SelectCase{ { Dir: reflect.SelectRecv, Chan: stopChanValue, }, { Dir: reflect.SelectSend, Chan: chanValue, Send: msg, }, }) switch chosen { case 0: return default: } } }() return stream }
func pMapChanInt(inChan reflect.Value, f interface{}, outChan reflect.Value) { fVal := reflect.ValueOf(f) val, ok := inChan.Recv() for ok { results := fVal.Call([]reflect.Value{val}) outChan.Send(results[0]) val, ok = inChan.Recv() } outChan.Close() }
func pMapDrainOutChans(outChans []reflect.Value, resultChan reflect.Value) { numChans := len(outChans) idx := 0 val, ok := outChans[idx].Recv() for ok { resultChan.Send(val) idx++ val, ok = outChans[idx%numChans].Recv() } resultChan.Close() }
func (t *Transport) toChan(cid uint64, cval reflect.Value) error { // Type check! woo if cval.Kind() != reflect.Chan { return fmt.Errorf("fatchan: cannot connect a %s - must be a channel", cval.Type()) } if cval.Type().ChanDir()&reflect.SendDir == 0 { return fmt.Errorf("fatchan: cannot connect a %s - recieve-only channel", cval.Type()) } // Register our channel recv := make(chan []byte, 32) reg := ®ister{ cid: cid, data: recv, done: make(done, 1), } t.query <- reg <-reg.done sid, cid := t.sid, reg.cid go func() { // Close the channel when we return defer cval.Close() // TODO(kevlar): Catch close of closed channel? // Unregister the channel when we return defer func() { select { case t.msg <- &unregister{cid: cid}: case <-time.After(10 * time.Millisecond): // TODO(kevlar): Is this prefereable to closing the channel // and catching the panic? I'm not sure. Would it be possible // to use a WaitGroup to know when to close the query channel? } }() etyp := cval.Type().Elem() for data := range recv { t.debug("[%d] new data %q", cid, data) v := reflect.New(etyp).Elem() if err := t.decodeValue(bytes.NewReader(data), v); err != nil { t.err(sid, cid, err) return } t.debug("[%d] sending %#v", cid, v.Interface()) cval.Send(v) } }() return nil }
func (t *Transport) toChan(cval reflect.Value) (uint64, uint64, error) { sid, cid := t.sid, atomic.AddUint64(&t.nextCID, 1) // Type check! woo if cval.Kind() != reflect.Chan { return sid, cid, fmt.Errorf("fatchan: cannot connect a %s - must be a channel", cval.Type()) } if cval.Type().ChanDir()&reflect.SendDir == 0 { return sid, cid, fmt.Errorf("fatchan: cannot connect a %s - recieve-only channel", cval.Type()) } // Register our data recv := make(chan []byte, 32) t.reg <- register{cid, recv} // Peruse the element type etyp := cval.Type().Elem() go func() { defer cval.Close() defer func() { t.unreg <- unregister{cid} }() for data := range recv { v := reflect.New(etyp).Elem() if err := t.decodeValue(bytes.NewReader(data), v); err != nil { t.err(t.sid, cid, err) return } cval.Send(v) } }() return sid, cid, nil }