diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2010-12-03 04:34:57 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2010-12-03 04:34:57 +0000 |
commit | 7a9389330e91acc3ed05deac2d198af25d13cf3c (patch) | |
tree | 38fe54a4f38ede5d949c915d66191f24a6fe5153 /libgo/go/http/persist.go | |
parent | 1aa6700378e5188a853c018256113ce6e1fb5c05 (diff) | |
download | gcc-7a9389330e91acc3ed05deac2d198af25d13cf3c.zip gcc-7a9389330e91acc3ed05deac2d198af25d13cf3c.tar.gz gcc-7a9389330e91acc3ed05deac2d198af25d13cf3c.tar.bz2 |
Add Go frontend, libgo library, and Go testsuite.
gcc/:
* gcc.c (default_compilers): Add entry for ".go".
* common.opt: Add -static-libgo as a driver option.
* doc/install.texi (Configuration): Mention libgo as an option for
--enable-shared. Mention go as an option for --enable-languages.
* doc/invoke.texi (Overall Options): Mention .go as a file name
suffix. Mention go as a -x option.
* doc/frontends.texi (G++ and GCC): Mention Go as a supported
language.
* doc/sourcebuild.texi (Top Level): Mention libgo.
* doc/standards.texi (Standards): Add section on Go language.
Move references for other languages into their own section.
* doc/contrib.texi (Contributors): Mention that I contributed the
Go frontend.
gcc/testsuite/:
* lib/go.exp: New file.
* lib/go-dg.exp: New file.
* lib/go-torture.exp: New file.
* lib/target-supports.exp (check_compile): Match // Go.
From-SVN: r167407
Diffstat (limited to 'libgo/go/http/persist.go')
-rw-r--r-- | libgo/go/http/persist.go | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/libgo/go/http/persist.go b/libgo/go/http/persist.go new file mode 100644 index 0000000..8bfc097 --- /dev/null +++ b/libgo/go/http/persist.go @@ -0,0 +1,303 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package http + +import ( + "bufio" + "container/list" + "io" + "net" + "os" + "sync" +) + +var ErrPersistEOF = &ProtocolError{"persistent connection closed"} + +// A ServerConn reads requests and sends responses over an underlying +// connection, until the HTTP keepalive logic commands an end. ServerConn +// does not close the underlying connection. Instead, the user calls Close +// and regains control over the connection. ServerConn supports pipe-lining, +// i.e. requests can be read out of sync (but in the same order) while the +// respective responses are sent. +type ServerConn struct { + c net.Conn + r *bufio.Reader + clsd bool // indicates a graceful close + re, we os.Error // read/write errors + lastBody io.ReadCloser + nread, nwritten int + lk sync.Mutex // protected read/write to re,we +} + +// NewServerConn returns a new ServerConn reading and writing c. If r is not +// nil, it is the buffer to use when reading c. +func NewServerConn(c net.Conn, r *bufio.Reader) *ServerConn { + if r == nil { + r = bufio.NewReader(c) + } + return &ServerConn{c: c, r: r} +} + +// Close detaches the ServerConn and returns the underlying connection as well +// as the read-side bufio which may have some left over data. Close may be +// called before Read has signaled the end of the keep-alive logic. The user +// should not call Close while Read or Write is in progress. +func (sc *ServerConn) Close() (c net.Conn, r *bufio.Reader) { + sc.lk.Lock() + defer sc.lk.Unlock() + c = sc.c + r = sc.r + sc.c = nil + sc.r = nil + return +} + +// Read returns the next request on the wire. An ErrPersistEOF is returned if +// it is gracefully determined that there are no more requests (e.g. after the +// first request on an HTTP/1.0 connection, or after a Connection:close on a +// HTTP/1.1 connection). Read can be called concurrently with Write, but not +// with another Read. +func (sc *ServerConn) Read() (req *Request, err os.Error) { + + sc.lk.Lock() + if sc.we != nil { // no point receiving if write-side broken or closed + defer sc.lk.Unlock() + return nil, sc.we + } + if sc.re != nil { + defer sc.lk.Unlock() + return nil, sc.re + } + sc.lk.Unlock() + + // Make sure body is fully consumed, even if user does not call body.Close + if sc.lastBody != nil { + // body.Close is assumed to be idempotent and multiple calls to + // it should return the error that its first invokation + // returned. + err = sc.lastBody.Close() + sc.lastBody = nil + if err != nil { + sc.lk.Lock() + defer sc.lk.Unlock() + sc.re = err + return nil, err + } + } + + req, err = ReadRequest(sc.r) + if err != nil { + sc.lk.Lock() + defer sc.lk.Unlock() + if err == io.ErrUnexpectedEOF { + // A close from the opposing client is treated as a + // graceful close, even if there was some unparse-able + // data before the close. + sc.re = ErrPersistEOF + return nil, sc.re + } else { + sc.re = err + return + } + } + sc.lastBody = req.Body + sc.nread++ + if req.Close { + sc.lk.Lock() + defer sc.lk.Unlock() + sc.re = ErrPersistEOF + return req, sc.re + } + return +} + +// Pending returns the number of unanswered requests +// that have been received on the connection. +func (sc *ServerConn) Pending() int { + sc.lk.Lock() + defer sc.lk.Unlock() + return sc.nread - sc.nwritten +} + +// Write writes a repsonse. To close the connection gracefully, set the +// Response.Close field to true. Write should be considered operational until +// it returns an error, regardless of any errors returned on the Read side. +// Write can be called concurrently with Read, but not with another Write. +func (sc *ServerConn) Write(resp *Response) os.Error { + + sc.lk.Lock() + if sc.we != nil { + defer sc.lk.Unlock() + return sc.we + } + sc.lk.Unlock() + if sc.nread <= sc.nwritten { + return os.NewError("persist server pipe count") + } + + if resp.Close { + // After signaling a keep-alive close, any pipelined unread + // requests will be lost. It is up to the user to drain them + // before signaling. + sc.lk.Lock() + sc.re = ErrPersistEOF + sc.lk.Unlock() + } + + err := resp.Write(sc.c) + if err != nil { + sc.lk.Lock() + defer sc.lk.Unlock() + sc.we = err + return err + } + sc.nwritten++ + + return nil +} + +// A ClientConn sends request and receives headers over an underlying +// connection, while respecting the HTTP keepalive logic. ClientConn is not +// responsible for closing the underlying connection. One must call Close to +// regain control of that connection and deal with it as desired. +type ClientConn struct { + c net.Conn + r *bufio.Reader + re, we os.Error // read/write errors + lastBody io.ReadCloser + nread, nwritten int + reqm list.List // request methods in order of execution + lk sync.Mutex // protects read/write to reqm,re,we +} + +// NewClientConn returns a new ClientConn reading and writing c. If r is not +// nil, it is the buffer to use when reading c. +func NewClientConn(c net.Conn, r *bufio.Reader) *ClientConn { + if r == nil { + r = bufio.NewReader(c) + } + return &ClientConn{c: c, r: r} +} + +// Close detaches the ClientConn and returns the underlying connection as well +// as the read-side bufio which may have some left over data. Close may be +// called before the user or Read have signaled the end of the keep-alive +// logic. The user should not call Close while Read or Write is in progress. +func (cc *ClientConn) Close() (c net.Conn, r *bufio.Reader) { + cc.lk.Lock() + c = cc.c + r = cc.r + cc.c = nil + cc.r = nil + cc.reqm.Init() + cc.lk.Unlock() + return +} + +// Write writes a request. An ErrPersistEOF error is returned if the connection +// has been closed in an HTTP keepalive sense. If req.Close equals true, the +// keepalive connection is logically closed after this request and the opposing +// server is informed. An ErrUnexpectedEOF indicates the remote closed the +// underlying TCP connection, which is usually considered as graceful close. +// Write can be called concurrently with Read, but not with another Write. +func (cc *ClientConn) Write(req *Request) os.Error { + + cc.lk.Lock() + if cc.re != nil { // no point sending if read-side closed or broken + defer cc.lk.Unlock() + return cc.re + } + if cc.we != nil { + defer cc.lk.Unlock() + return cc.we + } + cc.lk.Unlock() + + if req.Close { + // We write the EOF to the write-side error, because there + // still might be some pipelined reads + cc.lk.Lock() + cc.we = ErrPersistEOF + cc.lk.Unlock() + } + + err := req.Write(cc.c) + if err != nil { + cc.lk.Lock() + defer cc.lk.Unlock() + cc.we = err + return err + } + cc.nwritten++ + cc.lk.Lock() + cc.reqm.PushBack(req.Method) + cc.lk.Unlock() + + return nil +} + +// Pending returns the number of unanswered requests +// that have been sent on the connection. +func (cc *ClientConn) Pending() int { + cc.lk.Lock() + defer cc.lk.Unlock() + return cc.nwritten - cc.nread +} + +// Read reads the next response from the wire. A valid response might be +// returned together with an ErrPersistEOF, which means that the remote +// requested that this be the last request serviced. Read can be called +// concurrently with Write, but not with another Read. +func (cc *ClientConn) Read() (resp *Response, err os.Error) { + + cc.lk.Lock() + if cc.re != nil { + defer cc.lk.Unlock() + return nil, cc.re + } + cc.lk.Unlock() + + if cc.nread >= cc.nwritten { + return nil, os.NewError("persist client pipe count") + } + + // Make sure body is fully consumed, even if user does not call body.Close + if cc.lastBody != nil { + // body.Close is assumed to be idempotent and multiple calls to + // it should return the error that its first invokation + // returned. + err = cc.lastBody.Close() + cc.lastBody = nil + if err != nil { + cc.lk.Lock() + defer cc.lk.Unlock() + cc.re = err + return nil, err + } + } + + cc.lk.Lock() + m := cc.reqm.Front() + cc.reqm.Remove(m) + cc.lk.Unlock() + resp, err = ReadResponse(cc.r, m.Value.(string)) + if err != nil { + cc.lk.Lock() + defer cc.lk.Unlock() + cc.re = err + return + } + cc.lastBody = resp.Body + + cc.nread++ + + if resp.Close { + cc.lk.Lock() + defer cc.lk.Unlock() + cc.re = ErrPersistEOF // don't send any more requests + return resp, cc.re + } + return +} |