func newRequest(hr *http.Request, hc http.ResponseWriter) *Request { remoteAddrIP, remotePort := hr.RemoteAddr, 0 remoteAddr, _ := net.ResolveTCPAddr("tcp", hr.RemoteAddr) if remoteAddr != nil { remoteAddrIP = remoteAddr.IP.String() remotePort = remoteAddr.Port } req := Request{ Method: hr.Method, URL: hr.URL, Proto: hr.Proto, ProtoMajor: hr.ProtoMajor, ProtoMinor: hr.ProtoMinor, Headers: hr.Header, Body: hr.Body, Close: hr.Close, Host: hr.Host, Referer: hr.Referer(), UserAgent: hr.UserAgent(), FullParams: hr.Form, Cookie: hr.Cookies(), RemoteAddr: remoteAddrIP, RemotePort: remotePort, } return &req }
func referer(r *http.Request) (ref string) { ref = r.FormValue("ref") if len(ref) == 0 { ref = r.Referer() if len(ref) == 0 { ref = "/" } } return }
// sent a request off to twitter. Returns the response's body or an error. func send(url, method string, form map[string][]string, client *Client, body string) (result string, err os.Error) { req := new(http.Request) req.Method = method req.RawURL = url req.Host = URLHost req.Referer = "none" req.UserAgent = HTTPUserAgent req.Form = form req.Header = map[string]string{ "Connection": "Keep Alive", "Authorization": getAuthHeader(client), } req.Body = strings.NewReader(body) req.URL, err = http.ParseURL(req.RawURL) if err != nil { return "", err } // send request resp := new(http.Response) resp, err = http.Send(req) if err != nil { return "", err } result = getResponseBody(resp) return result, nil }
// Forward a request to another domain // By design Go doesn't let you define new methods on non-local types func (this *HttpClient) Forward(req *http.Request, newDomain string) (resp *http.Response, err os.Error) { log.Printf("req host: %v\n", req.Host) // TODO change the the domain back for _, cookie := range req.Cookie { log.Printf("req cookie: %v\n", cookie) } var base *http.URL /*var r *http.Response*/ url := (newDomain + req.URL.RawPath) log.Printf("forwarding to: " + url) redirectChecker := this.client.CheckRedirect if redirectChecker == nil { redirectChecker = defaultCheckRedirect } var via []*http.Request for redirect := 0; ; redirect++ { var nReq = http.Request{} nReq.Method = req.Method //log.Printf("HTTP method: " + nReq.Method) nReq.Header = req.Header //log.Printf("Headers: %v\n", nReq.Header) if base == nil { nReq.URL, err = http.ParseURL(url) } else { nReq.URL, err = base.ParseURL(url) } if err != nil { break } // Set the redirection referer headers if len(via) > 0 { // Add the Referer header. lastReq := via[len(via)-1] if lastReq.URL.Scheme != "https" { nReq.Referer = lastReq.URL.String() } err = redirectChecker(&nReq, via) if err != nil { break } } // TODO support for content in the body if we aren't // dealing with GET/HEAD requests. url = nReq.URL.String() // Wrapped the client so I could do that ...sigh... // Also, #Do i a wrapper around #send if resp, err = this.client.Do(&nReq); err != nil { break } if shouldRedirect(resp.StatusCode) { resp.Body.Close() if url = resp.Header.Get("Location"); url == "" { err = os.ErrorString(fmt.Sprintf("%d response missing Location header", resp.StatusCode)) break } base = req.URL via = append(via, &nReq) continue } // log.Printf("final URL: " + url) return } err = &http.URLError{req.Method, url, err} return }
// RequestFromMap creates an http.Request from CGI variables. // The returned Request's Body field is not populated. func RequestFromMap(params map[string]string) (*http.Request, os.Error) { r := new(http.Request) r.Method = params["REQUEST_METHOD"] if r.Method == "" { return nil, os.NewError("cgi: no REQUEST_METHOD in environment") } r.Proto = params["SERVER_PROTOCOL"] var ok bool r.ProtoMajor, r.ProtoMinor, ok = http.ParseHTTPVersion(r.Proto) if !ok { return nil, os.NewError("cgi: invalid SERVER_PROTOCOL version") } r.Close = true r.Trailer = http.Header{} r.Header = http.Header{} r.Host = params["HTTP_HOST"] r.Referer = params["HTTP_REFERER"] r.UserAgent = params["HTTP_USER_AGENT"] if lenstr := params["CONTENT_LENGTH"]; lenstr != "" { clen, err := strconv.Atoi64(lenstr) if err != nil { return nil, os.NewError("cgi: bad CONTENT_LENGTH in environment: " + lenstr) } r.ContentLength = clen } if ct := params["CONTENT_TYPE"]; ct != "" { r.Header.Set("Content-Type", ct) } // Copy "HTTP_FOO_BAR" variables to "Foo-Bar" Headers for k, v := range params { if !strings.HasPrefix(k, "HTTP_") || skipHeader[k] { continue } r.Header.Add(strings.Replace(k[5:], "_", "-", -1), v) } // TODO: cookies. parsing them isn't exported, though. if r.Host != "" { // Hostname is provided, so we can reasonably construct a URL, // even if we have to assume 'http' for the scheme. r.RawURL = "http://" + r.Host + params["REQUEST_URI"] url, err := http.ParseURL(r.RawURL) if err != nil { return nil, os.NewError("cgi: failed to parse host and REQUEST_URI into a URL: " + r.RawURL) } r.URL = url } // Fallback logic if we don't have a Host header or the URL // failed to parse if r.URL == nil { r.RawURL = params["REQUEST_URI"] url, err := http.ParseURL(r.RawURL) if err != nil { return nil, os.NewError("cgi: failed to parse REQUEST_URI into a URL: " + r.RawURL) } r.URL = url } // There's apparently a de-facto standard for this. // http://docstore.mik.ua/orelly/linux/cgi/ch03_02.htm#ch03-35636 if s := params["HTTPS"]; s == "on" || s == "ON" || s == "1" { r.TLS = &tls.ConnectionState{HandshakeComplete: true} } // Request.RemoteAddr has its port set by Go's standard http // server, so we do here too. We don't have one, though, so we // use a dummy one. r.RemoteAddr = net.JoinHostPort(params["REMOTE_ADDR"], "0") return r, nil }
func requestFromEnvironment(env map[string]string) (*http.Request, os.Error) { r := new(http.Request) r.Method = env["REQUEST_METHOD"] if r.Method == "" { return nil, os.NewError("cgi: no REQUEST_METHOD in environment") } r.Close = true r.Trailer = http.Header{} r.Header = http.Header{} r.Host = env["HTTP_HOST"] r.Referer = env["HTTP_REFERER"] r.UserAgent = env["HTTP_USER_AGENT"] // CGI doesn't allow chunked requests, so these should all be accurate: r.Proto = "HTTP/1.0" r.ProtoMajor = 1 r.ProtoMinor = 0 r.TransferEncoding = nil if lenstr := env["CONTENT_LENGTH"]; lenstr != "" { clen, err := strconv.Atoi64(lenstr) if err != nil { return nil, os.NewError("cgi: bad CONTENT_LENGTH in environment: " + lenstr) } r.ContentLength = clen r.Body = ioutil.NopCloser(io.LimitReader(os.Stdin, clen)) } // Copy "HTTP_FOO_BAR" variables to "Foo-Bar" Headers for k, v := range env { if !strings.HasPrefix(k, "HTTP_") || skipHeader[k] { continue } r.Header.Add(strings.Replace(k[5:], "_", "-", -1), v) } // TODO: cookies. parsing them isn't exported, though. if r.Host != "" { // Hostname is provided, so we can reasonably construct a URL, // even if we have to assume 'http' for the scheme. r.RawURL = "http://" + r.Host + env["REQUEST_URI"] url, err := http.ParseURL(r.RawURL) if err != nil { return nil, os.NewError("cgi: failed to parse host and REQUEST_URI into a URL: " + r.RawURL) } r.URL = url } // Fallback logic if we don't have a Host header or the URL // failed to parse if r.URL == nil { r.RawURL = env["REQUEST_URI"] url, err := http.ParseURL(r.RawURL) if err != nil { return nil, os.NewError("cgi: failed to parse REQUEST_URI into a URL: " + r.RawURL) } r.URL = url } return r, nil }
func (h *logHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) { // Strip port number from address addr := r.RemoteAddr if colon := strings.LastIndex(addr, ":"); colon != -1 { addr = addr[:colon] } lr := &logRecord{ time: time.UTC(), ip: addr, method: r.Method, rawpath: r.URL.RawPath, userAgent: r.UserAgent(), referer: r.Referer(), responseStatus: http.StatusOK, proto: r.Proto, ResponseWriter: rw, } h.handler.ServeHTTP(lr, r) h.ch <- lr }