Beispiel #1
0
/*
	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
}
Beispiel #2
0
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()
}
Beispiel #3
0
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()
}
Beispiel #4
0
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 := &register{
		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
}
Beispiel #5
0
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
}