Example #1
0
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
}
Example #2
0
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)
}