func (c *conn) Respond(status int, header web.StringsMap) (body web.ResponseBody) { if c.hijacked { log.Stderr("twister: Respond called on hijacked connection") return nil } if c.respondCalled { log.Stderr("twister: multiple calls to Respond") return nil } c.respondCalled = true c.requestErr = web.ErrInvalidState if _, found := header.Get(web.HeaderTransferEncoding); found { log.Stderr("twister: transfer encoding not allowed") header[web.HeaderTransferEncoding] = nil, false } if c.requestAvail > 0 { c.closeAfterResponse = true } c.chunked = true c.responseAvail = 0 if status == web.StatusNotModified { header[web.HeaderContentType] = nil, false header[web.HeaderContentLength] = nil, false c.chunked = false } else if s, found := header.Get(web.HeaderContentLength); found { c.responseAvail, _ = strconv.Atoi(s) c.chunked = false } else if c.req.ProtocolVersion < web.ProtocolVersion(1, 1) { c.closeAfterResponse = true } if c.closeAfterResponse { header.Set(web.HeaderConnection, "close") c.chunked = false } if c.chunked { header.Set(web.HeaderTransferEncoding, "chunked") } proto := "HTTP/1.0" if c.req.ProtocolVersion >= web.ProtocolVersion(1, 1) { proto = "HTTP/1.1" } statusString := strconv.Itoa(status) text, found := web.StatusText[status] if !found { text = "status code " + statusString } var b bytes.Buffer b.WriteString(proto) b.WriteString(" ") b.WriteString(statusString) b.WriteString(" ") b.WriteString(text) b.WriteString("\r\n") for key, values := range header { for _, value := range values { b.WriteString(key) b.WriteString(": ") b.WriteString(cleanHeaderValue(value)) b.WriteString("\r\n") } } b.WriteString("\r\n") if c.chunked { c.bw = bufio.NewWriter(chunkedWriter{c}) _, c.responseErr = c.netConn.Write(b.Bytes()) } else { c.bw = bufio.NewWriter(identityWriter{c}) c.bw.Write(b.Bytes()) } return c.bw }
func (ts *TwitterStream) connect() { var err os.Error log.Println("twitterstream: connecting to", ts.url) url, err := http.ParseURL(ts.url) if err != nil { panic("bad url: " + ts.url) } addr := url.Host if strings.LastIndex(addr, ":") <= strings.LastIndex(addr, "]") { if url.Scheme == "http" { addr = addr + ":80" } else { addr = addr + ":443" } } param := web.StringsMap{} for key, values := range ts.param { param[key] = values } ts.oauthClient.SignParam(ts.accessToken, "POST", ts.url, param) body := param.FormEncodedBytes() header := web.NewStringsMap( web.HeaderHost, url.Host, web.HeaderConnection, "close", // disable chunk encoding in response web.HeaderContentLength, strconv.Itoa(len(body)), web.HeaderContentType, "application/x-www-form-urlencoded") var request bytes.Buffer request.WriteString("POST ") request.WriteString(url.RawPath) request.WriteString(" HTTP/1.1\r\n") header.WriteHttpHeader(&request) request.Write(body) proxyURL, _ := http.ParseURL(os.Getenv("HTTP_PROXY")) if url.Scheme == "http" { if proxyURL != nil { ts.conn, err = net.Dial("tcp", "", proxyURL.Host) } else { ts.conn, err = net.Dial("tcp", "", addr) } if err != nil { ts.error("dial failed ", err) return } } else { if proxyURL != nil { var proxyConn net.Conn proxyConn, err = net.Dial("tcp", "", proxyURL.Host) if err == nil { ts.conn = tls.Client(proxyConn, nil) proxyConn.Write([]byte("CONNECT " + addr + " HTTP/1.1\r\n\r\n")) b := make([]byte, 1024) proxyConn.Read(b) } } else { ts.conn, err = tls.Dial("tcp", "", addr, nil) } if err != nil { ts.error("dial failed ", err) return } if err = ts.conn.(*tls.Conn).VerifyHostname(addr[:strings.LastIndex(addr, ":")]); err != nil { ts.error("could not verify host", err) return } } // Set timeout to detect dead connection. Twitter sends at least one line // to the response every 30 seconds. err = ts.conn.SetReadTimeout(60e9) if err != nil { ts.error("set read timeout failed", err) return } if _, err := ts.conn.Write(request.Bytes()); err != nil { ts.error("error writing request: ", err) return } ts.r, _ = bufio.NewReaderSize(ts.conn, 8192) p, err := ts.r.ReadSlice('\n') if err != nil { ts.error("error reading response: ", err) return } m := responseLineRegexp.FindSubmatch(p) if m == nil { ts.error("bad response line", nil) return } for { p, err = ts.r.ReadSlice('\n') if err != nil { ts.error("error reading header: ", err) return } if len(p) <= 2 { break } } if string(m[1]) != "200" { p, _ := ioutil.ReadAll(ts.r) log.Println(string(p)) ts.error("bad response code: "+string(m[1]), nil) return } log.Println("twitterstream: connected to", ts.url) }