Пример #1
0
// FileFetcher's Fetch() implementation
func (this *fileFetcherExtender) Fetch(u *url.URL, userAgent string, headRequest bool) (*http.Response, error) {
	var res *http.Response = new(http.Response)
	var req *http.Request
	var e error

	if req, e = http.NewRequest("GET", u.String(), nil); e != nil {
		panic(e)
	}

	// Prepare the pseudo-request
	req.Header.Add("User-Agent", userAgent)

	// Open the file specified as path in u, relative to testdata/[host]/
	f, e := os.Open(path.Join(FileFetcherBasePath, u.Host, u.Path))
	if e != nil {
		// Treat errors as 404s - file not found
		res.Status = "404 Not Found"
		res.StatusCode = 404
	} else {
		res.Status = "200 OK"
		res.StatusCode = 200
		res.Body = f
	}
	res.Request = req

	return res, e
}
Пример #2
0
func (c *URLCache) Restore(res *http.Response) {
	res.Status = c.CachedResponse.Status
	res.StatusCode = c.CachedResponse.StatusCode
	res.Header = c.CachedResponse.Header
	res.ContentLength = c.CachedResponse.ContentLength
	res.TransferEncoding = c.CachedResponse.TransferEncoding
	res.Body = &ClosableBuffer{bytes.NewReader(c.CachedBody)}
	res.Header.Set(CachedHeader, CachedHeaderVal)
	res.Header.Set(CachedMD5Header, c.MD5)
}
Пример #3
0
// ModifyResponse sets the status code to 400 Bad Request if a loop was
// detected in the request.
func (m *viaModifier) ModifyResponse(res *http.Response) error {
	ctx := martian.NewContext(res.Request)

	if err, _ := ctx.Get(viaLoopKey); err != nil {
		res.StatusCode = 400
		res.Status = http.StatusText(400)

		return err.(error)
	}

	return nil
}
Пример #4
0
// ModifyResponse runs resmod.ModifyResponse.
//
// If an error is returned from resmod.ModifyResponse it is returned.
func (m *Modifier) ModifyResponse(res *http.Response) error {
	ctx := martian.NewContext(res.Request)
	actx := auth.FromContext(ctx)

	err := m.resmod.ModifyResponse(res)

	if actx.Error() != nil {
		res.StatusCode = 403
		res.Status = http.StatusText(403)
	}

	return err
}
Пример #5
0
func (f *EtagFilter) FilterResponse(request *falcore.Request, res *http.Response) {
	request.CurrentStage.Status = 1 // Skipped (default)
	if if_none_match := request.HttpRequest.Header.Get("If-None-Match"); if_none_match != "" {
		if res.StatusCode == 200 && res.Header.Get("Etag") == if_none_match {
			res.StatusCode = 304
			res.Status = "304 Not Modified"
			res.Body.Close()
			res.Body = nil
			res.ContentLength = 0
			request.CurrentStage.Status = 0 // Success
		}
	}
}
Пример #6
0
func (res *HTTPResponseEvent) ToResponse(body bool) *http.Response {
	raw := new(http.Response)
	raw.Header = res.Headers
	raw.ProtoMajor = 1
	raw.ProtoMinor = 1
	raw.StatusCode = int(res.StatusCode)
	raw.Status = http.StatusText(raw.StatusCode)
	raw.TransferEncoding = res.Headers["TransferEncoding"]
	if body {
		raw.Body = NewHTTPBody(res.GetContentLength(), res.Content)
	}
	return raw
}
Пример #7
0
func TestResponse(t *testing.T) {
	under := new(http.Response)
	under.Status = "the status message"
	under.StatusCode = 392
	under.Header = make(http.Header)
	under.Header.Set("Blah", "blah blah")
	under.Body = ioutil.NopCloser(bytes.NewBufferString("some content"))

	var resp runtime.ClientResponse = response{under}
	assert.EqualValues(t, under.StatusCode, resp.Code())
	assert.Equal(t, under.Status, resp.Message())
	assert.Equal(t, "blah blah", resp.GetHeader("blah"))
	assert.Equal(t, under.Body, resp.Body())
}
Пример #8
0
// NewNotification returns a notification response with a specific body content.
func NewNotification(body *bytes.Buffer) *http.Response {
	resp := new(http.Response)
	resp.Status = "200 OK"
	resp.StatusCode = http.StatusOK
	resp.ProtoMajor = 1
	resp.ProtoMinor = 0
	resp.Body = ioutil.NopCloser(body)
	resp.ContentLength = int64(body.Len())
	resp.Header = map[string][]string{}
	resp.Header.Set("Content-Type", HTTPContentTypeHAPJson)

	// Will be ignored unfortunately and won't be fixed https://github.com/golang/go/issues/9304
	// Make sure to call FixProtocolSpecifier() instead
	resp.Proto = "EVENT/1.0"

	return resp
}
Пример #9
0
func (r *response) Response() *http.Response {
	if r.Data == nil {
		r.Data = new(bytes.Buffer)
	}
	out := new(http.Response)
	out.Status = fmt.Sprintf("%d %s", r.StatusCode, http.StatusText(r.StatusCode))
	out.StatusCode = r.StatusCode
	out.Proto = "HTTP/1.1"
	out.ProtoMajor = 1
	out.ProtoMinor = 1
	out.Header = r.Header
	out.Body = &readCloser{r.Data}
	out.ContentLength = int64(r.Data.Len())
	out.TransferEncoding = nil
	out.Close = true
	out.Trailer = make(http.Header)
	out.Request = r.Request
	return out
}
Пример #10
0
func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	r.Body.Close()

	conn, _, err := w.(http.Hijacker).Hijack()
	if err != nil {
		log.Println("Failed to hijack connection in ProxyConnections.", err)
		return
	}

	defer conn.Close()

	if _, ok := conn.(*tls.Conn); !ok {
		log.Println("Recieved a non-TLS connection in ProxyConnections.")
		return
	}

	// Send the connection accepted response.
	res := new(http.Response)
	res.Status = "200 Connection Established"
	res.StatusCode = http.StatusOK
	res.Proto = "HTTP/1.1"
	res.ProtoMajor = 1
	res.ProtoMinor = 1
	if err = res.Write(conn); err != nil {
		log.Println("Failed to send connection established message in ProxyConnections.", err)
		return
	}

	client, err := NewClientConn(conn, nil, 3, 1)
	if err != nil {
		log.Println("Error creating SPDY connection in ProxyConnections.", err)
		return
	}

	go client.Run()

	// Call user code.
	p.ProxyConnHandle(client)

	client.Close()
}
Пример #11
0
func resUnmarshal(p []byte) (string, *http.Response, error) {
	var handle string
	var res *http.Response
	unzipped, err := unzip(p)
	if err != nil {
		return handle, res, err.(*Error).escalate(errResUnmarshal)
	}
	in := new(response)
	in.Header = http.Header(make(map[string][]string))
	if err = json.Unmarshal(unzipped, in); err != nil {
		return handle, res, newError(errResUnmarshalJSON, err.Error())
	}
	handle = in.Handle
	res = new(http.Response)
	res.Body = &body{bytes.NewBuffer(in.Body)}
	res.ContentLength = int64(len(in.Body))
	res.Header = in.Header
	res.StatusCode = in.Code
	res.Status = http.StatusText(in.Code)
	return handle, res, nil
}
Пример #12
0
// New returns an event response for a characteristic from an accessory.
func New(a *accessory.Accessory, c *characteristic.Characteristic) (*http.Response, error) {
	body, err := Body(a, c)
	if err != nil {
		return nil, err
	}

	resp := new(http.Response)
	resp.Status = "200 OK"
	resp.StatusCode = http.StatusOK
	resp.ProtoMajor = 1
	resp.ProtoMinor = 0
	resp.Body = ioutil.NopCloser(body)
	resp.ContentLength = int64(body.Len())
	resp.Header = map[string][]string{}
	resp.Header.Set("Content-Type", netio.HTTPContentTypeHAPJson)
	// (brutella) Not sure if Date header must be set
	// resp.Header.Set("Date", netio.CurrentRFC1123Date())

	// Will be ignored unfortunately and won't be fixed https://github.com/golang/go/issues/9304
	// Make sure to call FixProtocolSpecifier() instead
	resp.Proto = "EVENT/1.0"

	return resp, nil
}
Пример #13
0
func (c *conn) largefetch(w *bufio.Writer, r *http.Request, first *http.Response) error {
	pos := 0
	length := 0
	step := 100000
	log.Printf("largefetch begin: %s", r.URL.Path)
	var err error
	if first == nil {
		first, _, pos, length, err = c.rangeRoundTrip(r, 0, 1000000)
		if err != nil {
			r.Body.Close()
			return fmt.Errorf("conn.largefetch(first roundtrip)>%s", err)
		}
		defer first.Body.Close()
	} else {
		contentRange := first.Header.Get("Content-Range")
		if contentRange == "" {
			r.Body.Close()
			return errors.New("conn.largefetch: empty content range")
		}
		m := rangeresp.FindStringSubmatch(contentRange)
		if len(m) != 4 {
			r.Body.Close()
			return errors.New("conn.largefetch: invalid content range")
		}
		pos, err = strconv.Atoi(m[2])
		if err != nil {
			r.Body.Close()
			return fmt.Errorf("conn.largefetch(convert pos)>%s", err)
		}
		length, err = strconv.Atoi(m[3])
		if err != nil {
			r.Body.Close()
			return fmt.Errorf("conn.largefetch(convert length)>%s", err)
		}
	}
	first.Header.Del("Content-Range")
	//first.ContentLength = int64(end - start + 1)
	first.Header.Set("Content-Length", strconv.Itoa(length))
	first.StatusCode = 200
	first.Status = http.StatusText(200)
	err = writeResponse(w, first)
	if err != nil {
		return fmt.Errorf("conn.largefetch>%s", err)
	}
	err = w.Flush()
	if err != nil {
		return fmt.Errorf("conn.largefetch(w.Flush)>%s", err)
	}
	if length-pos-1 == 0 {
		return nil
	}
	var seq sequencer
	task := make(chan int)
	errChan := make(chan error)
	for i := 0; i < 30; i++ {
		go func() {
			defer print("end--------")
			for n := range task {
				start_in := pos + 1 + step*n
				end_in := pos + step*(n+1)
				if end_in > length-1 {
					end_in = length - 1
				}
				var start_out, end_out int
				var resp *http.Response
				var err error
				for i := 0; i < 3; i++ {
					resp, start_out, end_out, _, err = c.rangeRoundTrip(r, start_in, end_in)
					if err == nil {
						break
					}
				}
				if err != nil {
					errChan <- fmt.Errorf("conn.largefetch.routine>%s", err)
					seq.Close()
					return
				}
				if start_out != start_in || end_out != end_in {
					errChan <- errors.New("conn.largefetch.routine: error range returned")
				}
				ok := seq.Start(uint(n))
				if !ok { // seq closed
					return
				}
				_, err = io.Copy(w, resp.Body)
				resp.Body.Close()
				if err != nil {
					errChan <- fmt.Errorf("conn.largefetch.routine(send conts body)>%s", err)
					seq.Close()
					return
				}
				err = w.Flush()
				if err != nil {
					errChan <- fmt.Errorf("conn.largefetch.routine(conts flush)>%s", err)
					seq.Close()
					return
				}
				seq.End(uint(n))
				errChan <- nil
			}
		}()
	}
	var nTask int
OUT2:
	for i := 0; i < (length-pos-2)/step+1; i++ {
	OUT1:
		for {
			select {
			case task <- i:
				nTask++
				break OUT1
			case err0 := <-errChan:
				if err0 != nil {
					err = fmt.Errorf("largefetch>%s", err0)
					close(task)
					nTask--
					break OUT2
				}
				nTask--
			}
		}
	}
	for nTask > 0 {
		err0 := <-errChan
		if err0 != nil && err == nil {
			err = fmt.Errorf("largefetch>%s", err0)
		}
		nTask--
	}
	close(errChan)
	r.Body.Close()
	log.Printf("largefetch end: %s", r.URL.Path)
	return err
}
Пример #14
0
func (h *TestHandler) PrepareResponse(resp *http.Response) error {
	resp.Status = "201"
	return nil
}
Пример #15
0
func toShadowD(w icap.ResponseWriter, req *icap.Request) {
	local_debug := false
	if strings.Contains(req.URL.RawQuery, "debug=1") {
		local_debug = true
	}

	h := w.Header()
	h.Set("ISTag", ISTag)
	h.Set("Service", "Shadower ICAP to WAF Connector")

	if *debug {
		fmt.Fprintln(os.Stderr, "Printing the full ICAP request")
		fmt.Fprintln(os.Stderr, req)
		fmt.Fprintln(os.Stderr, req.Request)
		fmt.Fprintln(os.Stderr, req.Response)
	}
	switch req.Method {
	case "OPTIONS":
		h.Set("Methods", "REQMOD")
		h.Set("Options-TTL", "1800")
		h.Set("Allow", "204, 206")
		h.Set("Preview", "0")
		h.Set("Transfer-Preview", "*")
		h.Set("Max-Connections", *maxConnections)
		h.Set("X-Include", "X-Client-Ip, X-Authenticated-Groups, X-Authenticated-User, X-Subscriber-Id")
		w.WriteHeader(200, nil, false)
	case "REQMOD":
		modified := false
		nullBody := false
		allow206 := false
		allow204 := false
		xclientip := false

		if _, allow204Exists := req.Header["Allow"]; allow204Exists {
			if strings.Contains(req.Header["Allow"][0], "204") {
				allow204 = true
			}
		}

		if _, allow206Exists := req.Header["Allow"]; allow206Exists {
			if strings.Contains(req.Header["Allow"][0], "206") {
				allow206 = true
			}
		}

		if _, xclientipExists := req.Header["X-Client-Ip"]; xclientipExists {
			if len(req.Header["X-Client-Ip"][0]) > 1 {
				xclientip = true
			}
		}

		if _, encapsulationExists := req.Header["Encapsulated"]; encapsulationExists {
			if strings.Contains(req.Header["Encapsulated"][0], "null-body=") {
				nullBody = true
			}
		}

		if *debug || local_debug {
			for k, v := range req.Header {
				fmt.Fprintln(os.Stderr, "The ICAP headers:")
				fmt.Fprintln(os.Stderr, "key size:", len(req.Header[k]))
				fmt.Fprintln(os.Stderr, "key:", k, "value:", v)
			}
		}

		_, _, _, _ = nullBody, allow206, modified, allow204
		if xclientip {
			req.Request.RemoteAddr = req.Header["X-Client-Ip"][0]
		}

		if wrongMethod(req) {
			if *debug {
				fmt.Println("This request has a", req.Request.Method, "method which is not being analyzed")
			}
			w.WriteHeader(204, nil, false)
			return
		}

		if *debug || local_debug {
			for k, v := range req.Request.Header {
				fmt.Fprintln(os.Stderr, "key:", k, "value:", v)
			}
		}

		// Send the request to ShadowD
		// If an attack(5,6) was declared then send a custom 500 page
		// If OK then send a 204 back
		var resStatus = 1
		shodowdres, err := shadowServer.SendToShadowd(req.Request)
		newmap := make(map[string]interface{})
		err = json.Unmarshal([]byte(shodowdres), &newmap)
		if err != nil {
			panic(err)
		}

		switch int(newmap["status"].(float64)) {
		case shadowd.STATUS_OK:
			if *debug || local_debug {
				fmt.Println("Request reported, OK")
			}
			w.WriteHeader(204, nil, false)
			return
		case shadowd.STATUS_BAD_REQUEST:
			resStatus = 400
		case shadowd.STATUS_BAD_SIGNATURE:
			resStatus = 503
		case shadowd.STATUS_BAD_JSON:
			resStatus = 504
		case shadowd.STATUS_ATTACK:
			resStatus = 505
		case shadowd.STATUS_CRITICAL_ATTACK:
			resStatus = 506
		default:
			resStatus = 500
		}

		resp := new(http.Response)
		resp.Status = "Internal Server Error"
		resp.StatusCode = resStatus
		resp.Proto = req.Request.Proto
		resp.ProtoMajor = req.Request.ProtoMajor
		resp.ProtoMinor = req.Request.ProtoMinor
		resp.Request = req.Request
		myHeaderMap := make(map[string][]string)
		resp.Header = myHeaderMap
		resp.Header.Set("X-Ngtech-Proxy", "Shadower")
		resp.Header.Set("X-Shadower", strconv.Itoa(resStatus))
		resp.Header.Set("Content-Type", "text/html")
		resp.Header.Set("Content-Length", strconv.Itoa(len(internalerrorpage)))
		w.WriteHeader(200, resp, true)
		io.WriteString(w, internalerrorpage)
		return

		if *debug {
			fmt.Println("end of the line 204 response!.. Shouldn't happen.")
		}
		w.WriteHeader(204, nil, false)
		return
	case "RESPMOD":
		w.WriteHeader(204, nil, false)
		return
	default:
		w.WriteHeader(405, nil, false)
		if *debug || local_debug {
			fmt.Fprintln(os.Stderr, "Invalid request method")
		}
	}
}
Пример #16
0
// ReplyWithStatus helper to write the http.Response status code and text.
func ReplyWithStatus(res *http.Response, code int) {
	res.StatusCode = code
	res.Status = strconv.Itoa(code) + " " + http.StatusText(code)
}
Пример #17
0
func (c *conn) unpackResponse(resp *http.Response) (*http.Response, error) {
	// read response
	bodyr := bufio.NewReader(resp.Body)
	compressed, err := bodyr.ReadByte()
	if err != nil {
		resp.Body.Close()
		return nil, fmt.Errorf("conn.unpackResponse(bodyr.ReadByte)>%s", err)
	}
	var status, lenHeaderEncoded, lenContent uint32
	var bodyReader io.ReadCloser
	if compressed == '1' {
		bodyReader, err = zlib.NewReader(bodyr)
		if err != nil {
			resp.Body.Close()
			return nil, fmt.Errorf("conn.unpackResponse(zlib.NewReader)>%s", err)
		}
		bodyReader = &closeWrap{closer: resp.Body, ReadCloser: bodyReader}
	} else {
		bodyReader = &closeWrap{closer: resp.Body, ReadCloser: ioutil.NopCloser(bodyr)}
	}

	// deal with header
	err = binary.Read(bodyReader, binary.BigEndian, &status)
	if err != nil {
		bodyReader.Close()
		return nil, fmt.Errorf("conn.unpackResponse(read status)>%s", err)
	}
	err = binary.Read(bodyReader, binary.BigEndian, &lenHeaderEncoded)
	if err != nil {
		bodyReader.Close()
		return nil, fmt.Errorf("conn.unpackResponse(read lenHeaderEncoded)>%s", err)
	}
	err = binary.Read(bodyReader, binary.BigEndian, &lenContent)
	if err != nil {
		bodyReader.Close()
		return nil, fmt.Errorf("conn.unpackResponse(read lenContent)>%s", err)
	}
	response := new(http.Response)
	response.StatusCode = int(status)
	response.Status = http.StatusText(int(status))
	response.ProtoMajor = 1
	response.ProtoMinor = 0
	bHeaderEncoded := make([]byte, lenHeaderEncoded)
	_, err = io.ReadFull(bodyReader, bHeaderEncoded)
	if err != nil {
		bodyReader.Close()
		return nil, fmt.Errorf("conn.unpackResponse(read header)>%s", err)
	}
	response.Header = make(http.Header)
	for _, h := range strings.Split(string(bHeaderEncoded), "&") {
		kv := strings.SplitN(h, "=", 2)
		if len(kv) != 2 {
			continue
		}
		value, err := hex.DecodeString(kv[1])
		if err != nil {
			bodyReader.Close()
			return nil, fmt.Errorf("conn.unpackResponse(hex.DecodeString(kv[1]))>%s", err)
		}
		if strings.Title(kv[0]) == "Set-Cookie" {
			for _, cookie := range strings.Split(string(value), "\r\nSet-Cookie: ") {
				response.Header.Add("Set-Cookie", cookie)
			}
		} else {
			response.Header.Add(strings.Title(kv[0]), string(value))
		}
	}
	response.Header.Set("Content-Length", strconv.Itoa(int(lenContent)))
	response.Body = bodyReader
	return response, nil
}
Пример #18
0
func (rt *RoundTripper) readResponse(c *e3x.Channel) (*http.Response, error) {
	var (
		r    = newClientPacketReadCloser(c)
		resp *http.Response
		err  error
	)

	{ // read the header
		var (
			headLenData [2]byte
			headLen     uint16
			headerData  []byte
			header      map[string]interface{}
		)

		_, err = io.ReadFull(r, headLenData[:])
		if err != nil {
			if err == io.EOF {
				return nil, io.ErrUnexpectedEOF
			}
			return nil, err
		}

		headLen = binary.BigEndian.Uint16(headLenData[:])
		headerData = make([]byte, headLen)
		_, err = io.ReadFull(r, headerData)
		if err != nil {
			if err == io.EOF {
				return nil, io.ErrUnexpectedEOF
			}
			return nil, err
		}

		err = json.Unmarshal(headerData, &header)
		if err != nil {
			return nil, err
		}

		resp = &http.Response{}

		if v, p := header[":status"]; p && v != nil {
			var i int

			switch w := v.(type) {
			case int:
				i = w
			case int64:
				i = int(w)
			case float32:
				i = int(w)
			case float64:
				i = int(w)
			}

			if i > 0 {
				resp.StatusCode = i
				resp.Status = http.StatusText(i)
			}

			delete(header, ":status")
		}
		if resp.StatusCode == 0 {
			return nil, &http.ProtocolError{"missing `status` header"}
		}

		resp.Header = make(http.Header, len(header))
		resp.Proto = "1.1"
		resp.ProtoMajor = 1
		resp.ProtoMinor = 1

		for k, v := range header {
			if s, ok := v.(string); !ok || s == "" {
				if k != "status" {
					resp.Header.Set(http.CanonicalHeaderKey(k), s)
				}
			}
		}
	}

	{ // set the body
		resp.Body = r
	}

	return resp, nil
}
Пример #19
0
// ModifyResponse overwrites the status text and code on an HTTP response and
// returns nil.
func (s *statusModifier) ModifyResponse(res *http.Response) error {
	res.StatusCode = s.statusCode
	res.Status = http.StatusText(s.statusCode)

	return nil
}
Пример #20
0
// RoundTrip takes a Request and returns a Response
//
// If there is a fresh Response already in cache, then it will be returned without connecting to
// the server.
//
// If there is a stale Response, then any validators it contains will be set on the new request
// to give the server a chance to respond with NotModified. If this happens, then the cached Response
// will be returned.
func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	req = cloneRequest(req)
	cacheKey := cacheKey(req)
	cacheableMethod := req.Method == "GET" || req.Method == "HEAD"
	var cachedResp *http.Response
	if cacheableMethod {
		cachedResp, err = CachedResponse(t.Cache, req)
	} else {
		// Need to invalidate an existing value
		t.Cache.Delete(cacheKey)
	}

	transport := t.Transport
	if transport == nil {
		transport = http.DefaultTransport
	}

	if cachedResp != nil && err == nil && cacheableMethod && req.Header.Get("range") == "" {
		if t.MarkCachedResponses {
			cachedResp.Header.Set(XFromCache, "1")
		}

		if varyMatches(cachedResp, req) {
			// Can only use cached value if the new request doesn't Vary significantly
			freshness := getFreshness(cachedResp.Header, req.Header)
			if freshness == fresh {
				return cachedResp, nil
			}

			if freshness == stale {
				// Add validators if caller hasn't already done so
				etag := cachedResp.Header.Get("etag")
				if etag != "" && req.Header.Get("etag") == "" {
					req.Header.Set("if-none-match", etag)
				}
				lastModified := cachedResp.Header.Get("last-modified")
				if lastModified != "" && req.Header.Get("last-modified") == "" {
					req.Header.Set("if-modified-since", lastModified)
				}
			}
		}

		resp, err = transport.RoundTrip(req)
		if err == nil && req.Method == "GET" && resp.StatusCode == http.StatusNotModified {
			// Replace the 304 response with the one from cache, but update with some new headers
			headersToMerge := getHopByHopHeaders(resp)
			for _, headerKey := range headersToMerge {
				cachedResp.Header.Set(headerKey, resp.Header.Get(headerKey))
			}
			cachedResp.Status = http.StatusText(http.StatusOK)
			cachedResp.StatusCode = http.StatusOK

			resp = cachedResp
		} else {
			if err != nil || resp.StatusCode != http.StatusOK {
				t.Cache.Delete(cacheKey)
			}
			if err != nil {
				return nil, err
			}
		}
	} else {
		reqCacheControl := parseCacheControl(req.Header)
		if _, ok := reqCacheControl["only-if-cached"]; ok {
			resp = newGatewayTimeoutResponse(req)
		} else {
			resp, err = transport.RoundTrip(req)
			if err != nil {
				return nil, err
			}
		}
	}

	reqCacheControl := parseCacheControl(req.Header)
	respCacheControl := parseCacheControl(resp.Header)

	if canStore(reqCacheControl, respCacheControl) {
		vary := resp.Header.Get("Vary")
		for _, varyKey := range strings.Split(vary, ",") {
			varyKey = http.CanonicalHeaderKey(strings.Trim(varyKey, " "))
			fakeHeader := "X-Varied-" + varyKey
			reqValue := req.Header.Get(varyKey)
			if reqValue != "" {
				resp.Header.Set(fakeHeader, reqValue)
			}
		}
		respBytes, err := httputil.DumpResponse(resp, true)
		if err == nil {
			t.Cache.Set(cacheKey, respBytes)
		}
	} else {
		t.Cache.Delete(cacheKey)
	}
	return resp, nil
}
Пример #21
0
// ReadResponse reads an HTTP response. The header is taken from h,
// which must include the SPDY-specific fields starting with ':'.
// If r is not nil, the body will be read from r. If t is not nil,
// the trailer will be taken from t after the body is finished.
func ReadResponse(h, t http.Header, r io.Reader, req *http.Request) (*http.Response, error) {
	for _, s := range badRespHeaderFields {
		if _, ok := h[s]; ok {
			return nil, &badStringError{"invalid header field", s}
		}
	}

	var err error
	resp := new(http.Response)
	resp.Request = req
	resp.Close = true
	resp.Header = make(http.Header)
	copyHeader(resp.Header, h)
	f := strings.SplitN(h.Get(":status"), " ", 2)
	var s string
	if len(f) > 1 {
		s = f[1]
	}
	resp.Status = f[0] + " " + s
	resp.StatusCode, err = strconv.Atoi(f[0])
	if err != nil {
		return nil, &badStringError{"malformed HTTP status code", f[0]}
	}
	resp.Proto = h.Get(":version")
	var ok bool
	resp.ProtoMajor, resp.ProtoMinor, ok = http.ParseHTTPVersion(resp.Proto)
	if !ok {
		return nil, &badStringError{"malformed HTTP version", resp.Proto}
	}

	realLength, err := fixLength(true, resp.StatusCode, req.Method, resp.Header)
	if err != nil {
		return nil, err
	}
	if req.Method == "HEAD" {
		if n, err := parseContentLength(h.Get("Content-Length")); err != nil {
			return nil, err
		} else {
			resp.ContentLength = n
		}
	} else {
		resp.ContentLength = realLength
	}

	switch {
	case realLength == 0:
		r = eofReader
	case realLength > 0:
		if r == nil {
			// TODO(kr): return error
		}
		r = io.LimitReader(r, realLength)
	}
	if r == nil {
		r = eofReader
	}
	body := &body{r: r}
	resp.Body = body
	if t != nil {
		body.hdr = resp
		body.trailer = t
	}
	return resp, nil
}
Пример #22
0
func filterByUrl(w icap.ResponseWriter, req *icap.Request) {
	h := w.Header()
	h.Set("ISTag", ISTag)
	h.Set("Service", "SquidBlocker filter ICAP service")

	if *debug {
		fmt.Fprintln(os.Stderr, "Printing the full ICAP request")
		fmt.Fprintln(os.Stderr, req)
		fmt.Fprintln(os.Stderr, req.Request)
	}
	switch req.Method {
	case "OPTIONS":
		h.Set("Methods", "REQMOD, RESPMOD")
		h.Set("Options-TTL", "1800")
		h.Set("Allow", "204")
		h.Set("Preview", "0")
		h.Set("Transfer-Preview", "*")
		h.Set("Max-Connections", "4000")
		h.Set("X-Include", "X-Client-IP, X-Authenticated-Groups, X-Authenticated-User, X-Subscriber-Id")
		w.WriteHeader(200, nil, false)
	case "REQMOD":

		// Check if the method is either OPTIONS\GET\POST\PUT etc
		// Also to analyse the request stucutre to verify what is the current one used
		// based on the RFC section at: http://tools.ietf.org/html/rfc7230#section-5.3
		// Treat the CONNECT method in a special way due to the fact that it cannot actually be modified.
		checkhost := ""
		port := "0"
		answer := *defaultAnswer
		var err error
		if *debug {
			fmt.Fprintln(os.Stderr, "Default CASE. Request to host: "+req.Request.URL.Host+", Request Method: "+req.Request.Method)
			fmt.Fprintln(os.Stderr, "The full url from the ICAP client request: "+req.Request.URL.String())
		}

		checkhost, port, err = net.SplitHostPort(req.Request.URL.Host)
		if err != nil {
			_ = err
			checkhost = req.Request.URL.Host
		}

		if port != "0" {
			if *debug {
				fmt.Fprintln(os.Stderr, "Rquest with port: "+port)
			}
		}

		if req.Request.Method == "CONNECT" && len(checkhost) > 0 && port != "0" {
			answer = check_tcp(checkhost, port)
		} else {
			answer = check(req.Request.URL.String())
		}

		if *debug {
			fmt.Fprintln(os.Stderr, "ERRlog: reporting answer size => "+strconv.Itoa(len(answer)))
			fmt.Fprintln(os.Stderr, "ERRlog: reporitng answer => "+answer+", for =>"+req.Request.URL.String())
		}

		// The next part comes to make sure that a DUNO respnse will be handled as the default answer/action
		if strings.HasPrefix(answer, "DUNO") {
			answer = *defaultAnswer + " rate=100 default_answer=yes"
			if *debug {
				fmt.Fprintln(os.Stderr, "ERRlog: reporting answer startsWith => \"DUNO\", taking default action")
				if len(*defaultAnswer) > 0 {
					fmt.Fprintln(os.Stderr, req.Request.URL.String()+" "+*defaultAnswer+" rate=40 default_answer=yes")
				} else {
					fmt.Fprintln(os.Stderr, req.Request.URL.String()+" OK state=DUNO")
				}
			}
		}

		if strings.HasPrefix(answer, "OK") {
			if *debug {
				fmt.Fprintln(os.Stderr, "OK response and sending 204 back")
			}
			w.WriteHeader(204, nil, false)
			return
		}
		if strings.HasPrefix(answer, "ERR") {
			if *debug {
				fmt.Fprintln(os.Stderr, "ERR response and sending 307 redirection back")
			}
			resp := new(http.Response)
			resp.Status = "307 SquidBlocker this url has been filtered!"
			resp.StatusCode = 307
			resp.Proto = "HTTP/1.1"
			resp.ProtoMajor = 1
			resp.ProtoMinor = 1
			myMap := make(map[string][]string)
			//What if it is a connect request
			myMap["Location"] = append(myMap["Location"], *block_page+"?url="+url.QueryEscape(req.Request.URL.String()))
			resp.Header = myMap
			//resp.Body = ioutil.NopCloser(bytes.NewBufferString(body))
			//resp.ContentLength = int64(len(body))
			resp.Request = req.Request
			w.WriteHeader(200, resp, true)
			return
		}
		if *debug {
			fmt.Fprintln(os.Stderr, "Unknown asnwer and scenario, not adapting the request")
		}
		w.WriteHeader(204, nil, false)
		return
	case "RESPMOD":
		w.WriteHeader(204, nil, false)
	default:
		w.WriteHeader(405, nil, false)
		if *debug {
			fmt.Fprintln(os.Stderr, "Invalid request method")
		}
	}
}
Пример #23
0
// RoundTrip takes a Request and returns a Response
//
// If there is a fresh Response already in cache, then it will be returned without connecting to
// the server.
//
// If there is a stale Response, then any validators it contains will be set on the new request
// to give the server a chance to respond with NotModified. If this happens, then the cached Response
// will be returned.
func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	cacheKey := cacheKey(req)
	cacheableMethod := req.Method == "GET" || req.Method == "HEAD"
	var cachedResp *http.Response
	if cacheableMethod {
		cachedResp, err = CachedResponse(t.Cache, req)
	} else {
		// Need to invalidate an existing value
		t.Cache.Delete(cacheKey)
	}

	transport := t.Transport
	if transport == nil {
		transport = http.DefaultTransport
	}

	if cachedResp != nil && err == nil && cacheableMethod && req.Header.Get("range") == "" {
		if t.MarkCachedResponses {
			cachedResp.Header.Set(XFromCache, "1")
		}

		if varyMatches(cachedResp, req) {
			// Can only use cached value if the new request doesn't Vary significantly
			freshness := getFreshness(cachedResp.Header, req.Header)
			if freshness == fresh {
				return cachedResp, nil
			}

			if freshness == stale {
				var req2 *http.Request
				// Add validators if caller hasn't already done so
				etag := cachedResp.Header.Get("etag")
				if etag != "" && req.Header.Get("etag") == "" {
					req2 = cloneRequest(req)
					req2.Header.Set("if-none-match", etag)
				}
				lastModified := cachedResp.Header.Get("last-modified")
				if lastModified != "" && req.Header.Get("last-modified") == "" {
					if req2 == nil {
						req2 = cloneRequest(req)
					}
					req2.Header.Set("if-modified-since", lastModified)
				}
				if req2 != nil {
					// Associate original request with cloned request so we can refer to
					// it in CancelRequest()
					t.setModReq(req, req2)
					req = req2
					defer func() {
						// Release req/clone mapping on error
						if err != nil {
							t.setModReq(req, nil)
						}
						if resp != nil {
							// Release req/clone mapping on body close/EOF
							resp.Body = &onEOFReader{
								rc: resp.Body,
								fn: func() { t.setModReq(req, nil) },
							}
						}
					}()
				}
			}
		}

		resp, err = transport.RoundTrip(req)
		if err == nil && req.Method == "GET" && resp.StatusCode == http.StatusNotModified {
			// Replace the 304 response with the one from cache, but update with some new headers
			endToEndHeaders := getEndToEndHeaders(resp.Header)
			for _, header := range endToEndHeaders {
				cachedResp.Header[header] = resp.Header[header]
			}
			cachedResp.Status = fmt.Sprintf("%d %s", http.StatusOK, http.StatusText(http.StatusOK))
			cachedResp.StatusCode = http.StatusOK

			resp = cachedResp
		} else if (err != nil || (cachedResp != nil && resp.StatusCode >= 500)) &&
			req.Method == "GET" && canStaleOnError(cachedResp.Header, req.Header) {
			// In case of transport failure and stale-if-error activated, returns cached content
			// when available
			cachedResp.Status = fmt.Sprintf("%d %s", http.StatusOK, http.StatusText(http.StatusOK))
			cachedResp.StatusCode = http.StatusOK
			return cachedResp, nil
		} else {
			if err != nil || resp.StatusCode != http.StatusOK {
				t.Cache.Delete(cacheKey)
			}
			if err != nil {
				if t.ResponseErrorFunc != nil {
					return t.ResponseErrorFunc(resp, err, req)
				}
				return nil, err
			}
		}
	} else {
		reqCacheControl := parseCacheControl(req.Header)
		if _, ok := reqCacheControl["only-if-cached"]; ok {
			resp = newGatewayTimeoutResponse(req)
		} else {
			resp, err = transport.RoundTrip(req)
			if err != nil {
				if t.ResponseErrorFunc != nil {
					return t.ResponseErrorFunc(resp, err, req)
				}
				return nil, err
			}
		}
	}

	reqCacheControl := parseCacheControl(req.Header)
	respCacheControl := parseCacheControl(resp.Header)

	if canStore(reqCacheControl, respCacheControl) {
		for _, varyKey := range headerAllCommaSepValues(resp.Header, "vary") {
			varyKey = http.CanonicalHeaderKey(varyKey)
			fakeHeader := "X-Varied-" + varyKey
			reqValue := req.Header.Get(varyKey)
			if reqValue != "" {
				resp.Header.Set(fakeHeader, reqValue)
			}
		}
		respBytes, err := httputil.DumpResponse(resp, true)
		if err == nil {
			t.Cache.Set(cacheKey, respBytes)
		}
	} else {
		t.Cache.Delete(cacheKey)
	}
	return resp, nil
}
Пример #24
0
// ModifyResponse overwrites the status text and code on an HTTP response and
// returns nil.
func (s *statusModifier) ModifyResponse(res *http.Response) error {
	res.StatusCode = s.statusCode
	res.Status = fmt.Sprintf("%d %s", s.statusCode, http.StatusText(s.statusCode))

	return nil
}
Пример #25
-1
func (r *Response) Response() *http.Response {
	out := new(http.Response)

	r.headerM.Lock()
	out.Status = fmt.Sprintf("%d %s", r.StatusCode, http.StatusText(r.StatusCode))
	out.StatusCode = r.StatusCode
	out.Header = r.Header
	r.headerM.Unlock()

	out.Proto = "HTTP/1.1"
	out.ProtoMajor = 1
	out.ProtoMinor = 1

	r.dataM.Lock()
	if r.data == nil {
		out.Body = &ReadCloser{new(bytes.Buffer)}
	} else if unrequestedGzip(r) {
		// User-agents MUST support gzip compression.
		// Regardless of the Accept-Encoding sent by the user-agent, the server may
		// always send content encoded with gzip or deflate encoding.
		r.data.Prep()
		out.Header.Del("Content-Encoding")
		out.Header.Del("Content-Length")
		out.ContentLength = -1
		out.Body = &gzipReader{body: r.data}
	} else {
		r.data.Prep()
		out.Body = r.data
		out.ContentLength = r.data.written
	}
	r.dataM.Unlock()

	out.TransferEncoding = nil
	out.Close = true
	out.Trailer = make(http.Header)
	out.Request = r.Request
	return out
}