// Handle the data from a single imported data stream, which will // have the form // (response, data)* // The response identifies by name which channel is receiving data. // TODO: allow an importer to send. func (imp *Importer) run() { // Loop on responses; requests are sent by ImportNValues() resp := new(response) for { if err := imp.decode(resp); err != nil { log.Stderr("importer response decode:", err) break } if resp.error != "" { log.Stderr("importer response error:", resp.error) // TODO: tear down connection break } imp.chanLock.Lock() ich, ok := imp.chans[resp.name] imp.chanLock.Unlock() if !ok { log.Stderr("unknown name in request:", resp.name) break } if ich.dir != Recv { log.Stderr("TODO: import send unimplemented") break } // Create a new value for each received item. val := reflect.MakeZero(ich.ptr.Type().(*reflect.PtrType).Elem()) ich.ptr.PointTo(val) if err := imp.decode(ich.ptr.Interface()); err != nil { log.Stderr("importer value decode:", err) return } ich.ch.Send(val) } }
func main() { // create the client. Here we are using a synchronous client. // Using the default ConnectionSpec, we are specifying the client to connect // to db 13 (e.g. SELECT 13), and a password of go-redis (e.g. AUTH go-redis) spec := redis.DefaultSpec().Db(13).Password("go-redis") client, e := redis.NewSynchClientWithSpec(spec) if e != nil { log.Stderr("failed to create the client", e) return } key := "examples/hello/user.name" value, e := client.Get(key) if e != nil { log.Stderr("error on Get", e) return } if value == nil { fmt.Printf("\nHello, don't believe we've met before!\nYour name? ") reader := bufio.NewReader(os.Stdin) user, _ := reader.ReadString(byte('\n')) if len(user) > 1 { user = user[0 : len(user)-1] value = []byte(user) client.Set(key, value) } else { fmt.Printf("vafanculo!\n") return } } fmt.Printf("Hey, ciao %s!\n", fmt.Sprintf("%s", value)) }
// WriteHeader sends an HTTP response header with status code. // If WriteHeader is not called explicitly, the first call to Write // will trigger an implicit WriteHeader(http.StatusOK). // Thus explicit calls to WriteHeader are mainly used to // send error codes. func (c *Conn) WriteHeader(code int) { if c.hijacked { log.Stderr("http: Conn.WriteHeader on hijacked connection") return } if c.wroteHeader { log.Stderr("http: multiple Conn.WriteHeader calls") return } c.wroteHeader = true c.status = code c.written = 0 if !c.Req.ProtoAtLeast(1, 0) { return } proto := "HTTP/1.0" if c.Req.ProtoAtLeast(1, 1) { proto = "HTTP/1.1" } codestring := strconv.Itoa(code) text, ok := statusText[code] if !ok { text = "status code " + codestring } io.WriteString(c.buf, proto+" "+codestring+" "+text+"\r\n") for k, v := range c.header { io.WriteString(c.buf, k+": "+v+"\r\n") } io.WriteString(c.buf, "\r\n") }
// Send all the data on a single channel to a client asking for a Recv func (client *expClient) serveRecv(req *request) { exp := client.exp resp := new(response) resp.name = req.name var ok bool exp.chanLock.Lock() ech, ok := exp.chans[req.name] exp.chanLock.Unlock() if !ok { resp.error = "no such channel: " + req.name log.Stderr("export:", resp.error) client.encode(resp, nil) // ignore any encode error, hope client gets it return } for { if ech.dir != Send { log.Stderr("TODO: recv export unimplemented") break } val := ech.ch.Recv() if err := client.encode(resp, val.Interface()); err != nil { log.Stderr("error encoding client response:", err) break } if req.count > 0 { req.count-- if req.count == 0 { break } } } }
// WriteHeader sends an HTTP response header with status code. // If WriteHeader is not called explicitly, the first call to Write // will trigger an implicit WriteHeader(http.StatusOK). // Thus explicit calls to WriteHeader are mainly used to // send error codes. func (c *Conn) WriteHeader(code int) { if c.hijacked { log.Stderr("http: Conn.WriteHeader on hijacked connection") return } if c.wroteHeader { log.Stderr("http: multiple Conn.WriteHeader calls") return } c.wroteHeader = true c.status = code if code == StatusNotModified { // Must not have body. c.header["Content-Type"] = "", false c.header["Transfer-Encoding"] = "", false } c.written = 0 if !c.Req.ProtoAtLeast(1, 0) { return } proto := "HTTP/1.0" if c.Req.ProtoAtLeast(1, 1) { proto = "HTTP/1.1" } codestring := strconv.Itoa(code) text, ok := statusText[code] if !ok { text = "status code " + codestring } io.WriteString(c.buf, proto+" "+codestring+" "+text+"\r\n") for k, v := range c.header { io.WriteString(c.buf, k+": "+v+"\r\n") } io.WriteString(c.buf, "\r\n") }
func proxyMuxer() { proxyMap := make(map[string]*proxy) for { select { case pp := <-queue: key := make([]byte, keyLen) // read key n, err := pp.r.Body.Read(key) if n != keyLen || err != nil { log.Stderr("Couldn't read key", key) continue } // find proxy p, ok := proxyMap[string(key)] if !ok { log.Stderr("Couldn't find proxy", key) continue } // handle p.handle(pp) case p := <-createQueue: proxyMap[p.key] = p } } }
func serveConnection(serverName string, secure bool, handler web.Handler, netConn net.Conn) { br := bufio.NewReader(netConn) for { c := conn{ serverName: serverName, secure: secure, netConn: netConn, br: br} if err := c.prepare(); err != nil { if err != os.EOF { log.Stderr("twister/sever: prepare failed", err) } break } handler.ServeWeb(c.req) if c.hijacked { return } if err := c.finish(); err != nil { log.Stderr("twister/sever: finish failed", err) break } if c.closeAfterResponse { break } } netConn.Close() }
func managementTask (c *asyncConnHdl, ctl workerCtl) (sig *interrupt_code, te *taskStatus) { /* TODO: connected: clearChannels(); startWokers(); disconnected: ? on_fault: disconnect(); goto disconnected; on_exit: ? */ log.Stderr("MGR: do task ..."); select { case stat:= <-c.feedback: log.Stderr("MGR: Feedback from one of my minions: ", stat); // do the shutdown for now -- TODO: try reconnect if stat.event == faulted { log.Stderr("MGR: Shutting down due to fault in ", stat.id); go func() { c.reqProcCtl <- stop; } (); go func() { c.rspProcCtl <- stop; } (); go func() { c.heartbeatCtl <- stop; } (); log.Stderr("MGR: Signal SHUTDOWN ... "); c.shutdown <- true; // stop self // TODO: should manager really be a task or a FSM? c.managerCtl <- stop; } case s := <- ctl: return &s, &ok_status; } return nil, &ok_status; }
// Manage sends and receives for a single client. For each (client Recv) request, // this will launch a serveRecv goroutine to deliver the data for that channel, // while (client Send) requests are handled as data arrives from the client. func (client *expClient) run() { hdr := new(header) req := new(request) error := new(error) for { if err := client.decode(hdr); err != nil { log.Stderr("error decoding client header:", err) // TODO: tear down connection return } switch hdr.payloadType { case payRequest: if err := client.decode(req); err != nil { log.Stderr("error decoding client request:", err) // TODO: tear down connection return } switch req.dir { case Recv: go client.serveRecv(*hdr, req.count) case Send: // Request to send is clear as a matter of protocol // but not actually used by the implementation. // The actual sends will have payload type payData. // TODO: manage the count? default: error.error = "export request: can't handle channel direction" log.Stderr(error.error, req.dir) client.encode(hdr, payError, error) } case payData: client.serveSend(*hdr) } } }
func heartbeatTask (c *asyncConnHdl, ctl workerCtl) (sig *interrupt_code, te *taskStatus) { var async AsyncConnection = c; select { case <-NewTimer (ns1Sec * c.super.spec.heartbeat): response, e := async.QueueRequest(&PING, [][]byte{}); if e != nil { return nil, &taskStatus {reqerr, e}; } stat, re, ok := response.future.(FutureBool).TryGet(ns1Sec); if re != nil { log.Stderr ("ERROR: Heartbeat recieved error response on PING"); return nil, &taskStatus {error, re}; } else if !ok { log.Stderr ("Warning: Heartbeat timeout on get PING response.") } else { // flytrap if stat != true { log.Stderr ("<BUG> Heartbeat recieved false stat on PING while response error was nil"); return nil, &taskStatus {error, NewError(SYSTEM_ERR, "BUG false stat on PING w/out error")}; } } case sig := <- ctl: return &sig, &ok_status; } return nil, &ok_status; }
func handleMessage(s net.Conn, reqChannel chan MCRequest) (ret bool) { log.Stdoutf("Handling a message...") hdrBytes := make([]byte, HDR_LEN) ret = false log.Stdoutf("Reading header...") bytesRead, err := io.ReadFull(s, hdrBytes) if err != nil || bytesRead != HDR_LEN { log.Stderr("Error reading message: %s (%d bytes)", err, bytesRead) return } req := grokHeader(hdrBytes) readContents(s, req) log.Stdout("Processing message %s", req) req.ResponseChannel = make(chan MCResponse) reqChannel <- req res := <-req.ResponseChannel ret = !res.Fatal if ret { log.Stdoutf("Got response %s", res) transmitResponse(s, req, res) } else { log.Stderr("Something went wrong, hanging up...") } return }
func main() { flag.Parse() listener, err := net.Listen("tcp", *listenAddr) if err != nil { panic(err) } conn, err := listener.Accept() if err != nil { panic(err) } buf := new(bytes.Buffer) // initiate new session and read key log.Stderr("Attempting connect", *destAddr) buf.Write([]byte(*destAddr)) resp, err := http.Post( "http://"+*httpAddr+"/create", "text/plain", buf) if err != nil { panic(err) } key, _ := ioutil.ReadAll(resp.Body) resp.Body.Close() log.Stderr("Connected, key", key) // ticker to set a rate at which to hit the server tick := time.NewTicker(int64(*tickInterval) * 1e6) read := makeReadChan(conn, bufSize) buf.Reset() for { select { case <-tick.C: // write buf to new http request req := bytes.NewBuffer(key) buf.WriteTo(req) resp, err := http.Post( "http://"+*httpAddr+"/ping", "application/octet-stream", req) if err != nil { log.Stderr(err.String()) continue } // write http response response to conn io.Copy(conn, resp.Body) resp.Body.Close() case b := <-read: buf.Write(b) } } }
// This could find a happy home in a generalized worker package ... // TODO func (c *asyncConnHdl) worker(id int, name string, task workerTask, ctl workerCtl, fb chan workerStatus) { var signal interrupt_code var tstat *taskStatus // todo: add startup hook for worker await_signal: log.Stdout(name, "_worker: await_signal.") signal = <-ctl on_interrupt: // log.Stdout(name, "_worker: on_interrupt: ", signal); switch signal { case stop: goto before_stop case pause: goto await_signal case start: goto work } work: // log.Stdout(name, "_worker: work!"); select { case signal = <-ctl: goto on_interrupt default: is, stat := task(c, ctl) // todo is a task context type if stat == nil { log.Stderr("<BUG> nil stat from worker ", name) } if stat.code != ok { log.Stdout(name, "_worker: task error!") tstat = stat goto on_error } else if is != nil { signal = *is goto on_interrupt } goto work } on_error: log.Stdout(name, "_worker: on_error!") // TODO: log it, send it, and go back to wait_start: log.Stderr(name, "_worker task raised error: ", tstat) fb <- workerStatus{id, faulted, tstat, &ctl} goto await_signal before_stop: log.Stdout(name, "_worker: before_stop!") // TODO: add shutdown hook for worker log.Stdout(name, "_worker: STOPPED!") }
func NewProxy(key, destAddr string) (p *proxy, err os.Error) { p = &proxy{C: make(chan proxyPacket), key: key} log.Stderr("Attempting connect", destAddr) p.conn, err = net.Dial("tcp", "", destAddr) if err != nil { return } p.conn.SetReadTimeout(readTimeout) log.Stderr("Connected", destAddr) return }
func signalListener(end chan bool) { for { if n := <-signal.Incoming; strings.HasPrefix(n.String(), "SIGINT") { log.Stderr("Received SIGINT, ending execution") end <- true return } else { log.Stderr("Received singal", n, "ignoring") } } }
func main() { var m int var user g9p.User var file *g9pc.File flag.Parse() user = g9p.OsUsers.Uid2User(os.Geteuid()) c, err := g9pc.Mount("tcp", *addr, "", user, nil) if err != nil { goto error } if flag.NArg() != 1 { log.Stderr("invalid arguments") return } file, err = c.FOpen(flag.Arg(0), g9p.OWRITE|g9p.OTRUNC) if err != nil { file, err = c.FCreate(flag.Arg(0), 0666, g9p.OWRITE) if err != nil { goto error } } buf := make([]byte, 8192) for { n, oserr := os.Stdin.Read(buf) if oserr != nil && oserr != io.EOF { err = &g9p.Error{oserr.String(), 0} goto error } if n == 0 { break } m, err = file.Write(buf[0:n]) if err != nil { goto error } if m != n { err = &g9p.Error{"short write", 0} goto error } } file.Close() return error: log.Stderr(fmt.Sprintf("Error: %v", err)) }
// Redis PING command. func (c *syncClient) Ping() (err Error) { if c == nil { log.Stderr("FAULT in synchclient.Ping(): why is c nil?") return NewError(SYSTEM_ERR, "c *syncClient is NIL!") } else if c.conn == nil { log.Stderr("FAULT in synchclient.Ping(): why is c.conn nil?") return NewError(SYSTEM_ERR, "c.conn *SynchConnection is NIL!") } _, err = c.conn.ServiceRequest(&PING, [][]byte{}) return }
// Wait for incoming connections, start a new runner for each func (exp *Exporter) listen() { for { conn, err := exp.listener.Accept() if err != nil { log.Stderr("exporter.listen:", err) break } log.Stderr("accepted call from", conn.RemoteAddr()) client := newClient(exp, conn) go client.run() } }
func (server *serverType) input(conn io.ReadWriteCloser) { dec := gob.NewDecoder(conn) enc := gob.NewEncoder(conn) sending := new(sync.Mutex) for { // Grab the request header. req := new(Request) err := dec.Decode(req) if err != nil { if err == os.EOF || err == io.ErrUnexpectedEOF { log.Stderr("rpc: ", err) break } s := "rpc: server cannot decode request: " + err.String() sendResponse(sending, req, invalidRequest, enc, s) continue } serviceMethod := strings.Split(req.ServiceMethod, ".", 0) if len(serviceMethod) != 2 { s := "rpc: service/method request ill:formed: " + req.ServiceMethod sendResponse(sending, req, invalidRequest, enc, s) continue } // Look up the request. server.Lock() service, ok := server.serviceMap[serviceMethod[0]] server.Unlock() if !ok { s := "rpc: can't find service " + req.ServiceMethod sendResponse(sending, req, invalidRequest, enc, s) continue } mtype, ok := service.method[serviceMethod[1]] if !ok { s := "rpc: can't find method " + req.ServiceMethod sendResponse(sending, req, invalidRequest, enc, s) continue } // Decode the argument value. argv := _new(mtype.argType) replyv := _new(mtype.replyType) err = dec.Decode(argv.Interface()) if err != nil { log.Stderr("rpc: tearing down", serviceMethod[0], "connection:", err) sendResponse(sending, req, replyv.Interface(), enc, err.String()) continue } go service.call(sending, mtype, req, argv, replyv, enc) } conn.Close() }
// ImportNValues imports a channel of the given type and specified direction // and then receives or transmits up to n values on that channel. A value of // n==0 implies an unbounded number of values. The channel to be bound to // the remote site's channel is provided in the call and may be of arbitrary // channel type. // Despite the literal signature, the effective signature is // ImportNValues(name string, chT chan T, dir Dir, pT T) // where T must be a struct, pointer to struct, etc. pT may be more indirect // than the value type of the channel (e.g. chan T, pT *T) but it must be a // pointer. // Example usage: // imp, err := NewImporter("tcp", "netchanserver.mydomain.com:1234") // if err != nil { log.Exit(err) } // ch := make(chan myType) // err := imp.ImportNValues("name", ch, Recv, new(myType), 1) // if err != nil { log.Exit(err) } // fmt.Printf("%+v\n", <-ch) // (TODO: Can we eliminate the need for pT?) func (imp *Importer) ImportNValues(name string, chT interface{}, dir Dir, pT interface{}, n int) os.Error { ch, err := checkChan(chT, dir) if err != nil { return err } // Make sure pT is a pointer (to a pointer...) to a struct. rt := reflect.Typeof(pT) if _, ok := rt.(*reflect.PtrType); !ok { return os.ErrorString("not a pointer:" + rt.String()) } if _, ok := reflect.Indirect(reflect.NewValue(pT)).(*reflect.StructValue); !ok { return os.ErrorString("not a pointer to a struct:" + rt.String()) } imp.chanLock.Lock() defer imp.chanLock.Unlock() _, present := imp.chans[name] if present { return os.ErrorString("channel name already being imported:" + name) } ptr := reflect.MakeZero(reflect.Typeof(pT)).(*reflect.PtrValue) imp.chans[name] = &importChan{ch, dir, ptr, n} // Tell the other side about this channel. req := new(request) req.name = name req.dir = dir req.count = n if err := imp.encode(req, nil); err != nil { log.Stderr("importer request encode:", err) return err } return nil }
func (c *asyncConnHdl) QueueRequest (cmd *Command, args [][]byte) (*PendingResponse, Error) { select { case <- c.shutdown: log.Stderr("<DEBUG> we're shutdown and not accepting any more requests ..."); return nil, NewError(SYSTEM_ERR, "Connection is shutdown."); default: } future := CreateFuture (cmd); request := &asyncRequestInfo{0, 0, cmd, nil, future, nil}; buff, e1 := CreateRequestBytes(cmd, args); if e1 == nil { request.outbuff = &buff; c.pendingReqs<- request; } else { errmsg:= fmt.Sprintf("Failed to create asynchrequest - %s aborted", cmd.Code); request.stat = inierr; request.error = NewErrorWithCause(SYSTEM_ERR, errmsg, e1); // only makes sense if using go ... request.future.(FutureResult).onError(request.error); return nil, request.error; // remove if restoring go } //}(); // done. return &PendingResponse {future}, nil; }
// Task: // process one pending response at a time // - can be interrupted while waiting on the pending responses queue // - buffered reader takes care of minimizing network io // // KNOWN BUG: // until we figure out what's the problem with read timeout, can not // be interrupted if hanging on a read func rspProcessingTask (c *asyncConnHdl, ctl workerCtl) (sig *interrupt_code, te *taskStatus) { var req asyncReqPtr; select { case sig := <- ctl: // interrupted return &sig, &ok_status; case req = <-c.pendingResps: // continue to process } // process response to asyncRequest reader:= c.super.reader; cmd:= req.cmd; resp, e3:= GetResponse (reader, cmd); if e3!= nil { // system error log.Stderr("<TEMP DEBUG> Request sent to faults chan on error in GetResponse: ", e3); req.stat = rcverr; req.error = NewErrorWithCause (SYSTEM_ERR, "GetResponse os.Error", e3); c.faults <- req; return nil, &taskStatus {rcverr, e3}; } SetFutureResult (req.future, cmd, resp); return nil, &ok_status; }
func main() { var response *http.Response var err error api := notifo.New("gotest", "a25c4f206494150bddf2e716705c8bedcad0cb16") //api.SetEndpoint("http://localhost:8000/v1/"); if response, err = api.SubscribeUser("devcamcar"); err != nil { log.Stderr("ERROR") log.Stderr(err) } else { dump, _ := httputil.DumpResponse(response, true) log.Stdout(response.StatusCode) log.Stdout(string(dump)) } }
// receives ?returnto=/replies etc. func CALLBACK(c *http.Conn, req *http.Request, auth_client *oauth.AuthClient) { log.Stderr("CALLBACK!"); req.ParseForm(); for k,v := range req.Header { log.Stderrf("header:%s:%s", k, v); } for k,vs := range req.Form { log.Stderrf("form:%s::", k); for i := range vs { log.Stderrf("::%s", vs[i]); } } var auth_token = req.FormValue("oauth_token"); var auth_verifier = req.FormValue("oauth_verifier"); log.Stderrf("CALLBACK:auth_token:%s:", auth_token); log.Stderrf("CALLBACK:auth_verifier:%s:", auth_verifier); user_info := auth_client.GetUserInfo(auth_token, auth_verifier); log.Stderrf("USER_INFO:"); for k,v := range user_info { log.Stderrf("k:%s v:%s", k, v); } session_service.StartSession(c, req, user_info); var url = "/"; returnto := req.FormValue("returnto"); if returnto != "" { url = returnto; } http.Redirect(c, url, http.StatusFound); // should be 303 instead of 302? }
func TWITTER_REPLIES(c *http.Conn, req *http.Request) { log.Stderrf(">REPLIES:"); s := session_service.GetSession(c,req); for k,v := range s.Data { log.Stderrf("session kv:%s:%s", k, v); } auth_token, atx := s.Data["oauth_token"]; if atx { log.Stderrf("TOKEN FOUND!"); auth_token_secret := s.Data["oauth_token_secret"]; r, finalUrl, err := twitter_client.MakeRequest("http://twitter.com/statuses/mentions.json", map[string]string{"oauth_token":auth_token}, auth_token_secret, false); //{"since_id":s.last_reply_id}) if err != nil { log.Stderrf(":REPLIES:err:%s",err); } else { log.Stderrf(":REPLIES:r:%s:finalUrl:%s", r, finalUrl); b, _ := io.ReadAll(r.Body); print ("REPLIES!"); str := bytes.NewBuffer(b).String(); println (str); j, ok, errtok := json.StringToJson(str); log.Stderr("REPLIES:j:%s:ok:%s:errtok:%s", j, ok, errtok); c.Write(strings.Bytes(j.String())); } } else { log.Stderrf("NO TOKEN FOUND!"); http.Redirect(c, "/login/twitter?returnto=/twitter/replies", http.StatusFound); // should be 303 instead of 302? } }
func main() { in := bufio.NewReader(os.Stdin) lines := vector.StringVector(make([]string, 0, 500)) for { for { l, err := in.ReadString('\n') if err != nil { log.Stderr("Error reading map, expected more input\n") return } if l == "go\n" { pw := ParseGameState(lines) DoTurn(pw) pw.EndTurn() break } else { lines.Push(l) } } lines = lines[0:0] } }
func ParseGameState(lines []string) *PlanetWars { pw := &PlanetWars{make([]*Planet, len(lines)), make([]*Fleet, len(lines))} pNum, fNum := 0, 0 for _, ln := range lines { switch ln[0] { case 'P': p := &Planet{ID: pNum} read, e := fmt.Sscanf(ln[2:], "%f %f %d %d %d", &p.X, &p.Y, &p.Owner, &p.NumShips, &p.GrowthRate) if read < 5 || e != nil { log.Stderrf("Bad line in input: %s\n", ln) } pw.Planets[pNum] = p pNum++ case 'F': f := &Fleet{ID: fNum} read, e := fmt.Sscanf(ln[2:], "%d %d %d %d %d", &f.Owner, &f.NumShips, &f.Source, &f.Dest, &f.TripLength, &f.TurnsRemaining) if read < 5 || e != nil { log.Stderrf("Bad line in input: %s\n", ln) } pw.Fleets[fNum] = f fNum++ default: log.Stderr("Error parsing gamestate: First char of line not 'P' or 'F'\n") return nil } } pw.Fleets = pw.Fleets[0:fNum] pw.Planets = pw.Planets[0:pNum] return pw }
func (client *Client) input() { var err os.Error; for err == nil { response := new(Response); err = client.dec.Decode(response); if err != nil { if err == os.EOF { err = io.ErrUnexpectedEOF; } break; } seq := response.Seq; client.mutex.Lock(); c := client.pending[seq]; client.pending[seq] = c, false; client.mutex.Unlock(); err = client.dec.Decode(c.Reply); c.Error = os.ErrorString(response.Error); // We don't want to block here. It is the caller's responsibility to make // sure the channel has enough buffer space. See comment in Go(). _ = c.Done <- c; // do not block } // Terminate pending calls. client.mutex.Lock(); client.shutdown = err; for _, call := range client.pending { call.Error = err; _ = call.Done <- call; // do not block } client.mutex.Unlock(); log.Stderr("client protocol error:", err); }
func withNewError(m string) os.Error { e := os.NewError(m) if debug() { log.Stderr(e) } return e }
// Write writes the data to the connection as part of an HTTP reply. // If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK) // before writing the data. func (c *Conn) Write(data []byte) (n int, err os.Error) { if c.hijacked { log.Stderr("http: Conn.Write on hijacked connection") return 0, ErrHijacked } if !c.wroteHeader { c.WriteHeader(StatusOK) } if len(data) == 0 { return 0, nil } c.written += int64(len(data)) // ignoring errors, for errorKludge // TODO(rsc): if chunking happened after the buffering, // then there would be fewer chunk headers. // On the other hand, it would make hijacking more difficult. if c.chunking { fmt.Fprintf(c.buf, "%x\r\n", len(data)) // TODO(rsc): use strconv not fmt } n, err = c.buf.Write(data) if err == nil && c.chunking { if n != len(data) { err = io.ErrShortWrite } if err == nil { io.WriteString(c.buf, "\r\n") } } return n, err }