| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112 |
- package httptoo
- import (
- "context"
- "io"
- "net/http"
- "sync"
- "github.com/anacrolix/missinggo"
- )
- type responseWriter struct {
- mu sync.Mutex
- r http.Response
- headerWritten missinggo.Event
- bodyWriter io.WriteCloser
- bodyClosed missinggo.SynchronizedEvent
- }
- var _ interface {
- http.ResponseWriter
- // We're able to emulate this easily enough.
- http.CloseNotifier
- } = &responseWriter{}
- // Use Request.Context.Done instead.
- func (me *responseWriter) CloseNotify() <-chan bool {
- ret := make(chan bool, 1)
- go func() {
- <-me.bodyClosed.C()
- ret <- true
- }()
- return ret
- }
- func (me *responseWriter) Header() http.Header {
- if me.r.Header == nil {
- me.r.Header = make(http.Header)
- }
- return me.r.Header
- }
- func (me *responseWriter) Write(b []byte) (int, error) {
- me.mu.Lock()
- if !me.headerWritten.IsSet() {
- me.writeHeader(200)
- }
- me.mu.Unlock()
- return me.bodyWriter.Write(b)
- }
- func (me *responseWriter) WriteHeader(status int) {
- me.mu.Lock()
- me.writeHeader(status)
- me.mu.Unlock()
- }
- func (me *responseWriter) writeHeader(status int) {
- if me.headerWritten.IsSet() {
- return
- }
- me.r.StatusCode = status
- me.headerWritten.Set()
- }
- func (me *responseWriter) runHandler(h http.Handler, req *http.Request) {
- var pr *io.PipeReader
- pr, me.bodyWriter = io.Pipe()
- me.r.Body = struct {
- io.Reader
- io.Closer
- }{pr, eventCloser{pr, &me.bodyClosed}}
- // Shouldn't be writing to the response after the handler returns.
- defer me.bodyWriter.Close()
- // Send a 200 if nothing was written yet.
- defer me.WriteHeader(200)
- // Wrap the context in the given Request with one that closes when either
- // the handler returns, or the response body is closed.
- ctx, cancel := context.WithCancel(req.Context())
- defer cancel()
- go func() {
- <-me.bodyClosed.C()
- cancel()
- }()
- h.ServeHTTP(me, req.WithContext(ctx))
- }
- type eventCloser struct {
- c io.Closer
- closed *missinggo.SynchronizedEvent
- }
- func (me eventCloser) Close() (err error) {
- err = me.c.Close()
- me.closed.Set()
- return
- }
- func RoundTripHandler(req *http.Request, h http.Handler) (*http.Response, error) {
- rw := responseWriter{}
- go rw.runHandler(h, req)
- <-rw.headerWritten.LockedChan(&rw.mu)
- return &rw.r, nil
- }
- type InProcRoundTripper struct {
- Handler http.Handler
- }
- func (me *InProcRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
- return RoundTripHandler(req, me.Handler)
- }
|