// writeCookies writes the wire representation of the cookies // to w. Each cookie is written on a separate "Cookie: " line. // This choice is made because HTTP parsers tend to have a limit on // line-length, so it seems safer to place cookies on separate lines. func writeCookies(w io.Writer, kk []*http.Cookie) os.Error { lines := make([]string, 0, len(kk)) var b bytes.Buffer for _, c := range kk { b.Reset() n := c.Name // TODO(petar): c.Value (below) should be unquoted if it is recognized as quoted fmt.Fprintf(&b, "%s=%s", http.CanonicalHeaderKey(n), c.Value) if len(c.Path) > 0 { fmt.Fprintf(&b, "; $Path=%s", http.URLEscape(c.Path)) } if len(c.Domain) > 0 { fmt.Fprintf(&b, "; $Domain=%s", http.URLEscape(c.Domain)) } if c.HttpOnly { fmt.Fprintf(&b, "; $HttpOnly") } lines = append(lines, "Cookie: "+b.String()+"\r\n") } sort.Strings(lines) for _, l := range lines { if _, err := io.WriteString(w, l); err != nil { return err } } return nil }
// matchMap returns true if the given key/value pairs exist in a given map. func matchMap(toCheck map[string]string, toMatch map[string][]string, canonicalKey bool) bool { for k, v := range toCheck { // Check if key exists. if canonicalKey { k = http.CanonicalHeaderKey(k) } if values, keyExists := toMatch[k]; !keyExists { return false } else if v != "" { // If value was defined as an empty string we only check that the // key exists. Otherwise we also check if the value exists. valueExists := false for _, value := range values { if v == value { valueExists = true break } } if !valueExists { return false } } } return true }
// Internal. Parses headers in NNTP articles. Most of this is stolen from the http package, // and it should probably be split out into a generic RFC822 header-parsing package. func (c *Conn) readHeader(r *bufio.Reader) (res *Article, err os.Error) { res = new(Article) res.Header = make(map[string][]string) for { var key, value string if key, value, err = readKeyValue(r); err != nil { return nil, err } if key == "" { break } key = http.CanonicalHeaderKey(key) // RFC 3977 says nothing about duplicate keys' values being equivalent to // a single key joined with commas, so we keep all values seperate. oldvalue, present := res.Header[key] if present { sv := vector.StringVector(oldvalue) sv.Push(value) res.Header[key] = []string(sv) } else { res.Header[key] = []string{value} } } return res, nil }
func Send(s Sender, req *http.Request) (resp *http.Response, err os.Error) { if s == nil { s = DefaultSender } req.ProtoMajor = 1 req.ProtoMinor = 1 header := req.Header req.Header = map[string]string{} for k, v := range header { req.Header[http.CanonicalHeaderKey(k)] = v } return s.Send(req) }
func newRequestCgi(headers http.Header, body io.Reader) *Request { var httpheader = make(http.Header) for header, value := range headers { if strings.HasPrefix(header, "Http_") { newHeader := header[5:] newHeader = strings.Replace(newHeader, "_", "-", -1) newHeader = http.CanonicalHeaderKey(newHeader) httpheader[newHeader] = value } } host := httpheader.Get("Host") method := headers.Get("REQUEST_METHOD") path := headers.Get("REQUEST_URI") port := headers.Get("SERVER_PORT") proto := headers.Get("SERVER_PROTOCOL") rawurl := "http://" + host + ":" + port + path url_, _ := url.Parse(rawurl) useragent := headers.Get("USER_AGENT") remoteAddr := headers.Get("REMOTE_ADDR") remotePort, _ := strconv.Atoi(headers.Get("REMOTE_PORT")) if method == "POST" { if ctype, ok := headers["CONTENT_TYPE"]; ok { httpheader["Content-Type"] = ctype } if clength, ok := headers["CONTENT_LENGTH"]; ok { httpheader["Content-Length"] = clength } } //read the cookies cookies := readCookies(httpheader) req := Request{ Method: method, RawURL: rawurl, URL: url_, Proto: proto, Host: host, UserAgent: useragent, Body: body, Headers: httpheader, RemoteAddr: remoteAddr, RemotePort: remotePort, Cookie: cookies, } return &req }
func newRequestCgi(headers map[string]string, body io.Reader) *Request { var httpheader = make(map[string]string) //copy HTTP_ variables for header, value := range headers { if strings.HasPrefix(header, "HTTP_") { newHeader := header[5:] newHeader = strings.Replace(newHeader, "_", "-", -1) newHeader = http.CanonicalHeaderKey(newHeader) httpheader[newHeader] = value } } host := httpheader["Host"] method, _ := headers["REQUEST_METHOD"] path, _ := headers["REQUEST_URI"] port, _ := headers["SERVER_PORT"] proto, _ := headers["SERVER_PROTOCOL"] rawurl := "http://" + host + ":" + port + path url, _ := http.ParseURL(rawurl) useragent, _ := headers["USER_AGENT"] remoteAddr, _ := headers["REMOTE_ADDR"] remotePort, _ := strconv.Atoi(headers["REMOTE_PORT"]) if method == "POST" { if ctype, ok := headers["CONTENT_TYPE"]; ok { httpheader["Content-Type"] = ctype } if clength, ok := headers["CONTENT_LENGTH"]; ok { httpheader["Content-Length"] = clength } } req := Request{ Method: method, RawURL: rawurl, URL: url, Proto: proto, Host: host, UserAgent: useragent, Body: body, Headers: httpheader, RemoteAddr: remoteAddr, RemotePort: remotePort, } return &req }
// writeSetCookies writes the wire representation of the set-cookies // to w. Each cookie is written on a separate "Set-Cookie: " line. // This choice is made because HTTP parsers tend to have a limit on // line-length, so it seems safer to place cookies on separate lines. func writeSetCookies(w io.Writer, kk []*http.Cookie) os.Error { if kk == nil { return nil } lines := make([]string, 0, len(kk)) var b bytes.Buffer for _, c := range kk { b.Reset() // TODO(petar): c.Value (below) should be unquoted if it is recognized as quoted fmt.Fprintf(&b, "%s=%s", http.CanonicalHeaderKey(c.Name), c.Value) if len(c.Path) > 0 { fmt.Fprintf(&b, "; Path=%s", http.URLEscape(c.Path)) } if len(c.Domain) > 0 { fmt.Fprintf(&b, "; Domain=%s", http.URLEscape(c.Domain)) } if len(c.Expires.Zone) > 0 { fmt.Fprintf(&b, "; Expires=%s", c.Expires.Format(time.RFC1123)) } if c.MaxAge >= 0 { fmt.Fprintf(&b, "; Max-Age=%d", c.MaxAge) } if c.HttpOnly { fmt.Fprintf(&b, "; HttpOnly") } if c.Secure { fmt.Fprintf(&b, "; Secure") } lines = append(lines, "Set-Cookie: "+b.String()+"\r\n") } sort.Strings(lines) for _, l := range lines { if _, err := io.WriteString(w, l); err != nil { return err } } return nil }
// RoundTrip issues a single HTTP request and returns its response. Per the // http.RoundTripper interface, RoundTrip only returns an error if there // was a problem with the request being malformed // (ErrInvalidFetchRequest) or the URL Fetch proxy fails (ErrFetch). // Note that HTTP response codes such as 5xx, 403, 404, etc are not // errors as far as the transport is concerned and will be returned // with err set to nil. func (t *Transport) RoundTrip(req *http.Request) (res *http.Response, err os.Error) { methNum, ok := pb.URLFetchRequest_RequestMethod_value[req.Method] if !ok { return nil, &ErrInvalidFetchRequest{"Unsupported method: " + req.Method, nil} } method := pb.URLFetchRequest_RequestMethod(methNum) freq := &pb.URLFetchRequest{ Method: &method, Url: proto.String(req.URL.String()), FollowRedirects: proto.Bool(false), // http.Client's responsibility MustValidateServerCertificate: proto.Bool(!t.AllowInvalidServerCertificate), } if t.DeadlineSeconds != 0 { freq.Deadline = proto.Float64(t.DeadlineSeconds) } for k, vals := range req.Header { for _, val := range vals { freq.Header = append(freq.Header, &pb.URLFetchRequest_Header{ Key: proto.String(k), Value: proto.String(val), }) } } if methodAcceptsRequestBody[req.Method] { freq.Payload, err = ioutil.ReadAll(req.Body) if err != nil { return nil, &ErrInvalidFetchRequest{"Failed to read body", err} } } fres := &pb.URLFetchResponse{} if err := t.Context.Call("urlfetch", "Fetch", freq, fres); err != nil { return nil, &ErrFetch{err.String()} } res = &http.Response{} res.StatusCode = int(*fres.StatusCode) res.Status = fmt.Sprintf("%d %s", res.StatusCode, statusCodeToText(res.StatusCode)) res.Header = http.Header(make(map[string][]string)) res.RequestMethod = req.Method // Faked: res.ProtoMajor = 1 res.ProtoMinor = 1 res.Proto = "HTTP/1.1" res.Close = true for _, h := range fres.Header { hkey := http.CanonicalHeaderKey(*h.Key) hval := *h.Value if hkey == "Content-Length" { // Will get filled in below for all but HEAD requests. if req.Method == "HEAD" { res.ContentLength, _ = strconv.Atoi64(hval) } continue } res.Header.Add(hkey, hval) } if req.Method != "HEAD" { res.ContentLength = int64(len(fres.Content)) } truncated := proto.GetBool(fres.ContentWasTruncated) res.Body = &bodyReader{content: fres.Content, truncated: truncated} return }
func getHeader(r *http.Request, key string) (value string) { return r.Header[http.CanonicalHeaderKey(key)] }
func (r *response) SetHeader(hdr, val string) { r.header[http.CanonicalHeaderKey(hdr)] = val }