// ParseForm parses the raw query from the URL. // // For POST or PUT requests, it also parses the request body as a form. // If the request Body's size has not already been limited by MaxBytesReader, // the size is capped at 10MB. // // ParseMultipartForm calls ParseForm automatically. // It is idempotent. func (r *Request) ParseForm() (err os.Error) { if r.Form != nil { return } if r.URL != nil { r.Form, err = url.ParseQuery(r.URL.RawQuery) } if r.Method == "POST" || r.Method == "PUT" { if r.Body == nil { return os.NewError("missing form body") } ct := r.Header.Get("Content-Type") ct, _, err := mime.ParseMediaType(ct) switch { case ct == "text/plain" || ct == "application/x-www-form-urlencoded" || ct == "": var reader io.Reader = r.Body maxFormSize := int64(1<<63 - 1) if _, ok := r.Body.(*maxBytesReader); !ok { maxFormSize = int64(10 << 20) // 10 MB is a lot of text. reader = io.LimitReader(r.Body, maxFormSize+1) } b, e := ioutil.ReadAll(reader) if e != nil { if err == nil { err = e } break } if int64(len(b)) > maxFormSize { return os.NewError("http: POST too large") } var newValues url.Values newValues, e = url.ParseQuery(string(b)) if err == nil { err = e } if r.Form == nil { r.Form = make(url.Values) } // Copy values into r.Form. TODO: make this smoother. for k, vs := range newValues { for _, value := range vs { r.Form.Add(k, value) } } case ct == "multipart/form-data": // handled by ParseMultipartForm (which is calling us, or should be) // TODO(bradfitz): there are too many possible // orders to call too many functions here. // Clean this up and write more tests. // request_test.go contains the start of this, // in TestRequestMultipartCallOrder. default: return &badStringError{"unknown Content-Type", ct} } } return err }
// ParseForm parses the raw query. // For POST requests, it also parses the request body as a form. // ParseMultipartForm calls ParseForm automatically. // It is idempotent. func (r *Request) ParseForm() (err os.Error) { if r.Form != nil { return } if r.URL != nil { r.Form, err = url.ParseQuery(r.URL.RawQuery) } if r.Method == "POST" { if r.Body == nil { return os.NewError("missing form body") } ct := r.Header.Get("Content-Type") switch strings.SplitN(ct, ";", 2)[0] { case "text/plain", "application/x-www-form-urlencoded", "": const maxFormSize = int64(10 << 20) // 10 MB is a lot of text. b, e := ioutil.ReadAll(io.LimitReader(r.Body, maxFormSize+1)) if e != nil { if err == nil { err = e } break } if int64(len(b)) > maxFormSize { return os.NewError("http: POST too large") } var newValues url.Values newValues, e = url.ParseQuery(string(b)) if err == nil { err = e } if r.Form == nil { r.Form = make(url.Values) } // Copy values into r.Form. TODO: make this smoother. for k, vs := range newValues { for _, value := range vs { r.Form.Add(k, value) } } case "multipart/form-data": // handled by ParseMultipartForm default: return &badStringError{"unknown Content-Type", ct} } } return err }
func TestParseForm(t *testing.T) { for i, test := range parseTests { form, err := url.ParseQuery(test.query) if err != nil { t.Errorf("test %d: Unexpected error: %v", i, err) continue } if len(form) != len(test.out) { t.Errorf("test %d: len(form) = %d, want %d", i, len(form), len(test.out)) } for k, evs := range test.out { vs, ok := form[k] if !ok { t.Errorf("test %d: Missing key %q", i, k) continue } if len(vs) != len(evs) { t.Errorf("test %d: len(form[%q]) = %d, want %d", i, k, len(vs), len(evs)) continue } for j, ev := range evs { if v := vs[j]; v != ev { t.Errorf("test %d: form[%q][%d] = %q, want %q", i, k, j, v, ev) } } } } }
func handle(w http.ResponseWriter, r *http.Request) { params, err := url.ParseQuery(r.URL.RawQuery) check(err) w.Header().Add("Access-Control-Allow-Origin", "*") w.Header().Add( "Access-Control-Allow-Methods", "OPTIONS, HEAD, GET, POST, PUT, DELETE", ) switch r.Method { case "OPTIONS": case "HEAD": case "GET": get(w, r) case "POST": if len(params["_method"]) > 0 && params["_method"][0] == "DELETE" { delete(w, r) } else { post(w, r) } case "DELETE": delete(w, r) default: http.Error(w, "501 Not Implemented", http.StatusNotImplemented) } }
// Generates the canonical string-to-sign for dsocial services. // You shouldn't need to use this directly. func (p *signer) Canonicalize(req *http.Request) (out string, err os.Error) { fv, err := url.ParseQuery(req.URL.RawQuery) if err == nil { out = strings.Join([]string{req.Method, req.Host, req.URL.Path, SortedEscape(fv)}, "\n") } return }
func UpdateLocation(w http.ResponseWriter, r *http.Request, validator auth.Validator, manager chan ManagerRequest) { auth_ok, client := validator.Validate(w, r) if !auth_ok { return } params, _ := url.ParseQuery(r.URL.RawQuery) location, err := ParseLocationFromRequest(params) if err != nil { http.Error(w, *err, http.StatusBadRequest) return } log.Printf("Got update request for %s with timestamp %d", *client, location.timestamp) // Reject timestamp from future. now := time.Seconds() * 1000 if location.timestamp > now { location.timestamp = now } out := make(chan bool, 1) updateRequest := &UpdateLocationRequest{*client, location, out} manager <- updateRequest _ = <-out response := "ok" fmt.Fprintf(w, response) }
func camliMode(req *http.Request) string { // TODO-GO: this is too hard to get at the GET Query args on a // POST request. m, err := url.ParseQuery(req.URL.RawQuery) if err != nil { return "" } if mode, ok := m["camli.mode"]; ok && len(mode) > 0 { return mode[0] } return "" }
func (o *OAuthClient) GetAccessToken(requestToken *RequestToken, OAuthVerifier string) (*AccessToken, os.Error) { if requestToken == nil || requestToken.OAuthToken == "" || requestToken.OAuthTokenSecret == "" { return nil, os.NewError("Invalid Request token") } nonce := getNonce(40) params := map[string]string{ "oauth_nonce": nonce, "oauth_token": requestToken.OAuthToken, "oauth_verifier": OAuthVerifier, "oauth_signature_method": "HMAC-SHA1", "oauth_timestamp": strconv.Itoa64(time.Seconds()), "oauth_consumer_key": o.ConsumerKey, "oauth_version": "1.0", } base := signatureBase("POST", requestTokenUrl.Raw, params) signature := signRequest(base, o.ConsumerSecret, requestToken.OAuthTokenSecret) params["oauth_signature"] = URLEscape(signature) authBuf := bytes.NewBufferString("OAuth ") i := 0 for k, v := range params { authBuf.WriteString(fmt.Sprintf("%s=%q", k, v)) if i < len(params)-1 { authBuf.WriteString(", ") } i++ } request := httplib.Post(accessTokenUrl.Raw) request.Header("Authorization", authBuf.String()) request.Body("") resp, err := request.AsString() tokens, err := url.ParseQuery(resp) if err != nil { return nil, err } at := AccessToken{ OAuthTokenSecret: tokens["oauth_token_secret"][0], OAuthToken: tokens["oauth_token"][0], UserId: tokens["user_id"][0], ScreenName: tokens["screen_name"][0], } return &at, nil }
// Given the returned response from the access token request, pull out the // access token and token secret. Store a copy of any other values returned, // too, since some services (like Twitter) return handy information such // as the username. func (c *UserConfig) parseAccessToken(response *http.Response) os.Error { defer response.Body.Close() body, err := ioutil.ReadAll(response.Body) if err != nil { return err } params, err := url.ParseQuery(string(body)) tokenKey := params.Get("oauth_token") tokenSecret := params.Get("oauth_token_secret") if tokenKey == "" || tokenSecret == "" { return os.NewError("No token or secret found") } c.AccessTokenKey = tokenKey c.AccessTokenSecret = tokenSecret c.AccessValues = params return nil }
func Poll(w http.ResponseWriter, r *http.Request, validator auth.Validator, manager chan ManagerRequest) { auth_ok, client := validator.Validate(w, r) if !auth_ok { return } params, _ := url.ParseQuery(r.URL.RawQuery) outFormat, err := query.GetQueryParam(params, "output") if err != nil { jsonFormat := "json" outFormat = &jsonFormat } timeout, err := query.GetInt32QueryParam(params, "timeout") if err != nil { timeout = kDefaultPollTimeoutSec } if timeout <= 0 || timeout > 60*60 { http.Error(w, fmt.Sprintf("Invalid timeout: %d", timeout), http.StatusBadRequest) return } log.Printf("Got poll request from %s with timeout %d", *client, timeout) out := make(chan *map[string]Location, 1) manager <- &WaitForUpdatesRequest{out} timeoutChan := make(chan bool, 1) go func() { time.Sleep(int64(timeout) * 1e9) timeoutChan <- true }() select { case locations := <-out: log.Printf("Sending update on poll request from %s", *client) if *outFormat == "proto" { w.Header().Add("Content-type", "application/octet-stream") w.Write(PrintLocationsAsProto(*locations)) } else { w.Write([]byte(PrintLocationsAsJson(*locations))) } case <-timeoutChan: log.Printf("Poll request from %s timed out", *client) http.Error(w, "Poll request timed out, please try again", http.StatusRequestTimeout) } }
func GetLocations(w http.ResponseWriter, r *http.Request, validator auth.Validator, manager chan ManagerRequest) { auth_ok, client := validator.Validate(w, r) if !auth_ok { return } log.Printf("Got locations request from %s", *client) params, _ := url.ParseQuery(r.URL.RawQuery) outFormat, err := query.GetQueryParam(params, "output") locations := GetAllLocations(manager) if err == nil && *outFormat == "proto" { w.Header().Add("Content-type", "application/octet-stream") w.Write(PrintLocationsAsProto(*locations)) } else { w.Write([]byte(PrintLocationsAsJson(*locations))) } }
func handleUploads(r *http.Request) (fileInfos []*FileInfo) { fileInfos = make([]*FileInfo, 0) mr, err := r.MultipartReader() check(err) r.Form, err = url.ParseQuery(r.URL.RawQuery) check(err) part, err := mr.NextPart() for err == nil { if name := part.FormName(); name != "" { if part.FileName() != "" { fileInfos = append(fileInfos, handleUpload(r, part)) } else { r.Form[name] = append(r.Form[name], getFormValue(part)) } } part, err = mr.NextPart() } return }
// Given the returned response from a Request token request, parse out the // appropriate request token and secret fields. func (c *UserConfig) parseRequestToken(response *http.Response) os.Error { defer response.Body.Close() body, err := ioutil.ReadAll(response.Body) if err != nil { return err } params, err := url.ParseQuery(string(body)) tokenKey := params.Get("oauth_token") tokenSecret := params.Get("oauth_token_secret") if tokenKey == "" || tokenSecret == "" { return os.NewError("No token or secret found") } c.RequestTokenKey = tokenKey c.RequestTokenSecret = tokenSecret if params.Get("oauth_callback_confirmed") == "false" { return os.NewError("OAuth callback not confirmed") } return nil }
func JavascriptHandler(w http.ResponseWriter, r *http.Request) { params, _ := url.ParseQuery(r.URL.RawQuery) name, err := query.GetQueryParam(params, "name") if err != nil { http.Error(w, *err, http.StatusBadRequest) return } reg := regexp.MustCompile("[^a-z_]") if reg.MatchString(*name) { http.Error(w, "Bad filename", http.StatusBadRequest) return } data, osErr := ioutil.ReadFile(*name + ".js") if osErr != nil { http.Error(w, "File not found", http.StatusBadRequest) return } w.Header().Add("Content-type", "text/javascript") w.Write(data) }
func (o *OAuthClient) GetRequestToken(callback string) *RequestToken { nonce := getNonce(40) params := map[string]string{ "oauth_nonce": nonce, "oauth_callback": URLEscape(callback), "oauth_signature_method": "HMAC-SHA1", "oauth_timestamp": strconv.Itoa64(time.Seconds()), "oauth_consumer_key": o.ConsumerKey, "oauth_version": "1.0", } base := signatureBase("POST", requestTokenUrl.Raw, params) signature := signRequest(base, o.ConsumerSecret, "") params["oauth_signature"] = URLEscape(signature) authBuf := bytes.NewBufferString("OAuth ") i := 0 for k, v := range params { authBuf.WriteString(fmt.Sprintf("%s=%q", k, v)) if i < len(params)-1 { authBuf.WriteString(", ") } i++ } request := httplib.Post(requestTokenUrl.Raw) request.Header("Authorization", authBuf.String()) request.Body("") resp, err := request.AsString() tokens, err := url.ParseQuery(resp) if err != nil { println(err.String()) } confirmed, _ := strconv.Atob(tokens["oauth_callback_confirmed"][0]) rt := RequestToken{ OAuthTokenSecret: tokens["oauth_token_secret"][0], OAuthToken: tokens["oauth_token"][0], OAuthCallbackConfirmed: confirmed, } return &rt }
// Modifies the request for signing // if expiresIn is set to 0, a Timestamp will be used, otherwise an expiration. func (p *signer) SignRequest(req *http.Request, expiresIn int64) { qstring, err := url.ParseQuery(req.URL.RawQuery) if err != nil { return } qstring["SignatureVersion"] = []string{DEFAULT_SIGNATURE_VERSION} if _, ok := qstring["SignatureMethod"]; !ok || len(qstring["SignatureMethod"]) == 0 { qstring["SignatureMethod"] = []string{DEFAULT_SIGNATURE_METHOD} } if expiresIn > 0 { qstring["Expires"] = []string{strconv.Itoa64(time.Seconds() + expiresIn)} } else { qstring["Timestamp"] = []string{time.UTC().Format(dm.UTC_DATETIME_FORMAT)} } qstring["Signature"] = nil, false qstring["DSOCAccessKeyId"] = []string{p.accessKey} var signature []byte req.URL.RawQuery = qstring.Encode() canonicalizedStringToSign, err := p.Canonicalize(req) if err != nil { return } //log.Printf("String-to-sign: '%s'", canonicalizedStringToSign) switch qstring["SignatureMethod"][0] { case SIGNATURE_METHOD_HMAC_SHA256: signature, err = p.SignEncoded(crypto.SHA256, canonicalizedStringToSign, base64.StdEncoding) case SIGNATURE_METHOD_HMAC_SHA1: signature, err = p.SignEncoded(crypto.SHA1, canonicalizedStringToSign, base64.StdEncoding) default: err = os.NewError("Unknown SignatureMethod:" + req.Form.Get("SignatureMethod")) } if err == nil { req.URL.RawQuery += "&" + url.Values{"Signature": []string{string(signature)}}.Encode() req.RawURL = req.URL.String() } return }
// Verify that the url given match a successfull authentication // Return: // * true if authenticated, false otherwise // * The Claimed identifier if authenticated // * Eventually an error func Verify(url_ string) (grant bool, identifier string, err error) { grant = false identifier = "" err = nil var values url.Values values, err = url.ParseQuery(url_) if err != nil { return false, "", err } // The value of "openid.return_to" matches the URL of the current request (Section 11.1) // To be implemented in a global way // Discovered information matches the information in the assertion (Section 11.2) // An assertion has not yet been accepted from this OP with the same value for "openid.response_nonce" (Section 11.3) // The signature on the assertion is valid and all fields that are required to be signed are signed (Section 11.4) return VerifyValues(values) }
// ReadRequestBody parses the URL for the AJAX parameters func (qx *queryCodec) ReadRequestBody(args interface{}) (err os.Error) { defer func() { qx.seq = 0 }() if args == nil { if qx.Query.Req.Body != nil { qx.Query.Req.Body.Close() } return nil } a := args.(*Args) // Save request method (GET, POST, PUT, UPDATE, etc.) a.Method = qx.Query.Req.Method // Decode URL arguments a.Query, err = url.ParseQuery(qx.Query.Req.URL.RawQuery) if err != nil { return err } // Decode JSON body a.Body = make(map[string]interface{}) if qx.Query.Req.Body != nil { dec := json.NewDecoder(qx.Query.Req.Body) // We don't care if the decode is successful. // The user will do their own complaining if they are missing expected arguments. dec.Decode(a.Body) qx.Query.Req.Body.Close() } // Read the cookies associated with the request a.Cookies = qx.Query.Req.Cookies() return nil }
// Parse query string: /path?q=val1&id=val2 func getParams(r *http.Request) url.Values { p, _ := url.ParseQuery(r.URL.RawQuery) return p }
func SmushFiles(response http.ResponseWriter, request *http.Request) { var requiredParams = []string{"name", "source"} if request.Method != "POST" { http.Error(response, "Must post from main Smush form", http.StatusInternalServerError) return } if !RequireParams(request, requiredParams) { http.Error(response, "Missing required params", http.StatusInternalServerError) return } var source []string source = request.Form["source"] sourceStrings := make([]string, len(source)) i := 0 // move the source urls into a new array so we can preserve the order, but skip blank entries for _, v := range source { if v != "" { sourceStrings[i] = v i++ } } compileParamString := "compilation_level=SIMPLE_OPTIMIZATIONS" compileParamString += "&output_format=text" compileParamString += "&output_info=compiled_code" for _, v := range sourceStrings[:i] { compileParamString += "&code_url=" + v } compileParams, _ := url.ParseQuery(compileParamString) compileResponse, _ := http.PostForm(compilerURL, compileParams) result, _ := ReadWholeFile(compileResponse.Body) if len(result) == 1 { http.Error(response, "Something went wrong with the Closure Compiler", http.StatusInternalServerError) return } currentTime := time.LocalTime().Nanoseconds() dirName := "out/" + strconv.Itoa64(currentTime) fileName := dirName + "/" + request.Form["name"][0] + ".min.js" if err := os.Mkdir(dirName, uint32(0777)); err != nil { http.Error(response, "Couldn't create output folder", http.StatusInternalServerError) return } outputFile, err := os.Create(fileName) if err != nil { http.Error(response, "Couldn't create output file", http.StatusInternalServerError) return } _, err = outputFile.WriteString(result) if err != nil { http.Error(response, "Couldn't write output to file", http.StatusInternalServerError) return } http.Redirect(response, request, "/"+fileName, http.StatusFound) }
// Checks whether the request has a signature, validates it if it does // and returns an error if signature is present but not valid func (p *signer) CheckSignature(req *http.Request) (hasSignature, validSignature bool, err os.Error) { qstring, err := url.ParseQuery(req.URL.RawQuery) if err != nil { err = ErrorInvalidURI return } if qstring.Get("Signature") == "" || qstring.Get("DSOCAccessKeyId") == "" { return } hasSignature = true now := time.UTC().Seconds() if expiresStr := qstring.Get("Expires"); expiresStr != "" { expiresAt, _ := strconv.Atoi64(expiresStr) if expiresAt < now { err = ErrorRequestExpired return } } else if timestampStr := qstring.Get("Timestamp"); timestampStr != "" { timestamp, _ := time.Parse(dm.UTC_DATETIME_FORMAT, timestampStr) if timestamp == nil || timestamp.Seconds()-MAX_VALID_TIMESTAMP_IN_SECONDS > now || timestamp.Seconds()+MAX_VALID_TIMESTAMP_IN_SECONDS < now { err = ErrorTimestampTooOld return } } else { err = ErrorExpiresOrTimestampRequired return } if qstring.Get("SignatureVersion") != "" && qstring.Get("SignatureVersion") != DEFAULT_SIGNATURE_VERSION { err = ErrorInvalidSignatureVersion return } var h crypto.Hash signatureMethod := qstring.Get("SignatureMethod") if signatureMethod == "" { signatureMethod = DEFAULT_SIGNATURE_METHOD } switch signatureMethod { case SIGNATURE_METHOD_HMAC_SHA256: h = crypto.SHA256 case SIGNATURE_METHOD_HMAC_SHA1: h = crypto.SHA1 default: err = ErrorInvalidSignatureMethod return } originalSignature := qstring.Get("Signature") qstring["Signature"] = nil, false qstring["DSOCAccessKeyId"] = []string{p.accessKey} var signature []byte req.URL.RawQuery = qstring.Encode() canonicalizedStringToSign, err := p.Canonicalize(req) if err != nil { return } //log.Printf("String-to-sign: '%s'", canonicalizedStringToSign) signature, err = p.SignEncoded(h, canonicalizedStringToSign, base64.StdEncoding) if err != nil || string(signature) != originalSignature { err = ErrorSignatureDoesNotMatch } else { validSignature = true } return }
func (p *GoogleContactService) RetrieveGroups(client oauth2_client.OAuth2Client, ds DataStoreService, dsocialUserId string, next NextToken) ([]*Group, NextToken, os.Error) { var m url.Values if next == nil { } else if s, ok := next.(string); ok { if s != "" { if strings.HasPrefix(s, "https://www.google.com/") { uri, err := url.Parse(s) if err == nil { q, err := url.ParseQuery(uri.RawQuery) if err == nil { m = q } } } if m == nil { m = make(url.Values) m.Add("q", s) } } } else if maxResults, ok := next.(int); ok { m = make(url.Values) m.Add("max-results", strconv.Itoa(maxResults)) } else if maxResults, ok := next.(int64); ok { m = make(url.Values) m.Add("max-results", strconv.Itoa64(maxResults)) } else if gq, ok := next.(*google.GroupQuery); ok { m = make(url.Values) if gq.Alt != "" { m.Add("alt", gq.Alt) } if gq.Q != "" { m.Add("q", gq.Q) } if gq.MaxResults > 0 { m.Add("max-results", strconv.Itoa64(gq.MaxResults)) } if gq.StartIndex > 0 { m.Add("start-index", strconv.Itoa64(gq.StartIndex)) } if gq.UpdatedMin != "" { m.Add("updated-min", gq.UpdatedMin) } if gq.OrderBy != "" { m.Add("orderby", gq.OrderBy) } if gq.ShowDeleted { m.Add("showdeleted", "true") } if gq.RequireAllDeleted { m.Add("requirealldeleted", "true") } if gq.SortOrder != "" { m.Add("sortorder", gq.SortOrder) } } resp, err := google.RetrieveGroups(client, m) var theNextToken NextToken = nil if resp != nil && resp.Feed != nil && resp.Feed.Links != nil && len(resp.Feed.Links) > 0 { for _, link := range resp.Feed.Links { if link.Rel == "next" { theNextToken = link.Href } } } if resp == nil || resp.Feed == nil || resp.Feed.Entries == nil || len(resp.Feed.Entries) == 0 || err != nil { return make([]*Group, 0), theNextToken, err } groups := make([]*Group, len(resp.Feed.Entries)) externalServiceId := p.ServiceId() userInfo, err := client.RetrieveUserInfo() externalUserId := userInfo.Guid() var useErr os.Error = nil for i, googleGroup := range resp.Feed.Entries { externalGroupId := googleGroup.GroupId() var origDsocialGroup *dm.Group = nil dsocialGroupId := "" if len(externalGroupId) > 0 { dsocialGroupId, err = ds.DsocialIdForExternalGroupId(externalServiceId, externalUserId, dsocialUserId, externalGroupId) if err != nil { if useErr == nil { useErr = err } continue } if dsocialGroupId != "" { origDsocialGroup, _, err = ds.RetrieveDsocialGroupForExternalGroup(externalServiceId, externalUserId, externalGroupId, dsocialUserId) if err != nil { if useErr == nil { useErr = err } continue } } else { ds.StoreExternalGroup(externalServiceId, externalUserId, dsocialUserId, externalGroupId, &googleGroup) } } var dsocialGroup *dm.Group = dm.GoogleGroupToDsocial(&googleGroup, origDsocialGroup, dsocialUserId) groups[i] = &Group{ ExternalServiceId: p.ServiceId(), ExternalUserId: googleGroup.GroupUserId(), ExternalGroupId: googleGroup.GroupId(), DsocialUserId: dsocialUserId, DsocialGroupId: dsocialGroupId, Value: dsocialGroup, } } return groups, theNextToken, useErr }
func handler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) c.Infof("Requested URL: %v", r.URL) error := func(msg string) { reply := map[string]string{ "error": msg, } resp, _ := json.Marshal(reply) w.WriteHeader(500) w.Write(resp) } query, _ := url.ParseQuery(r.URL.RawQuery) req_agent := r.Header.Get("User-Agent") req_url, ok_url := query["url"] if !ok_url { error("required parameters: url") return } c.Infof("Handling request for %s, agent: %s\n", req_url, req_agent) parsed_url, err := url.Parse(req_url[0]) if err != nil { error("Invalid URL: " + err.String()) return } robotsUrl := "http://" + parsed_url.Host + "/robots.txt" client := urlfetch.Client(c) resp, err := client.Get(robotsUrl) if err != nil { error("cannot fetch robots.txt: " + err.String()) return } c.Infof("Fetched robots.txt: %s, status code: %s \n", robotsUrl, resp.StatusCode) defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) robots, err := robotstxt.FromResponse(resp.StatusCode, string(body), true) if err != nil { error("cannot parse robots file: " + err.String()) return } allow, err := robots.TestAgent(parsed_url.Path, req_agent) if (err != nil) || !allow { reply := map[string]string{ "status": "disallowed", } resp, _ := json.Marshal(reply) w.WriteHeader(400) w.Write(resp) return } w.Header().Set("Location", req_url[0]) w.WriteHeader(http.StatusFound) }