예제 #1
0
func process(buf []byte) {
	// First byte indicate payload type, possible values:
	//  1 - Request
	//  2 - Response
	//  3 - ReplayedResponse
	payloadType := buf[0]
	headerSize := bytes.IndexByte(buf, '\n') + 1
	header := buf[:headerSize-1]

	// Header contains space separated values of: request type, request id, and request start time (or round-trip time for responses)
	meta := bytes.Split(header, []byte(" "))
	// For each request you should receive 3 payloads (request, response, replayed response) with same request id
	reqID := string(meta[1])
	payload := buf[headerSize:]

	Debug("Received payload:", string(buf))

	switch payloadType {
	case '1': // Request
		if bytes.Equal(proto.Path(payload), []byte("/token")) {
			originalTokens[reqID] = []byte{}
			Debug("Found token request:", reqID)
		} else {
			token, vs, _ := proto.PathParam(payload, []byte("token"))

			if vs != -1 { // If there is GET token param
				if alias, ok := tokenAliases[string(token)]; ok {
					// Rewrite original token to alias
					payload = proto.SetPathParam(payload, []byte("token"), alias)

					// Copy modified payload to our buffer
					buf = append(buf[:headerSize], payload...)
				}
			}
		}

		// Emitting data back
		os.Stdout.Write(encode(buf))
	case '2': // Original response
		if _, ok := originalTokens[reqID]; ok {
			// Token is inside response body
			secureToken := proto.Body(payload)
			originalTokens[reqID] = secureToken
			Debug("Remember origial token:", string(secureToken))
		}
	case '3': // Replayed response
		if originalToken, ok := originalTokens[reqID]; ok {
			delete(originalTokens, reqID)
			secureToken := proto.Body(payload)
			tokenAliases[string(originalToken)] = secureToken

			Debug("Create alias for new token token, was:", string(originalToken), "now:", string(secureToken))
		}
	}
}
예제 #2
0
파일: middleware_test.go 프로젝트: npk/gor
func TestFakeSecureService(t *testing.T) {
	var resp, token []byte

	wg := new(sync.WaitGroup)

	server := NewFakeSecureService(wg, func(path string, status int, resp []byte) {
	})
	defer server.Close()

	wg.Add(3)

	client := NewHTTPClient(server.URL, &HTTPClientConfig{Debug: true})
	resp, _ = client.Get("/token")
	token = proto.Body(resp)

	// Right token
	resp, _ = client.Get("/secure?token=" + string(token))
	if !bytes.Equal(proto.Status(resp), []byte("202")) {
		t.Error("Valid token should return status 202:", string(proto.Status(resp)))
	}

	// Wrong tokens forbidden
	resp, _ = client.Get("/secure?token=wrong")
	if !bytes.Equal(proto.Status(resp), []byte("403")) {
		t.Error("Wrong token should returns status 403:", string(proto.Status(resp)))
	}

	wg.Wait()
}
예제 #3
0
func TestInputHTTPLargePayload(t *testing.T) {
	wg := new(sync.WaitGroup)
	quit := make(chan int)

	// Generate 1000kb file
	dd := exec.Command("dd", "if=/dev/urandom", "of=/tmp/large", "bs=1MB", "count=4")
	err := dd.Run()
	if err != nil {
		log.Fatal("dd error:", err)
	}

	input := NewHTTPInput(":0")
	output := NewTestOutput(func(data []byte) {
		if len(proto.Body(payloadBody(data))) != 4000000 {
			t.Error("Should receive full file")
		}
		wg.Done()
	})
	Plugins.Inputs = []io.Reader{input}
	Plugins.Outputs = []io.Writer{output}

	go Start(quit)

	wg.Add(1)
	address := strings.Replace(input.listener.Addr().String(), "[::]", "127.0.0.1", -1)
	curl := exec.Command("curl", "http://"+address, "--data-binary", "@/tmp/large")
	err = curl.Run()
	if err != nil {
		log.Fatal("curl error:", err)
	}

	wg.Wait()
	close(quit)
}
예제 #4
0
파일: output_kafka.go 프로젝트: buger/gor
func (o *KafkaOutput) Write(data []byte) (n int, err error) {
	headers := make(map[string]string)
	proto.ParseHeaders([][]byte{data}, func(header []byte, value []byte) bool {
		headers[string(header)] = string(value)
		return true
	})

	req := payloadBody(data)

	kafkaMessage := KafkaMessage{
		ReqURL:     string(proto.Path(req)),
		ReqMethod:  string(proto.Method(req)),
		ReqBody:    string(proto.Body(req)),
		ReqHeaders: headers,
	}
	jsonMessage, _ := json.Marshal(&kafkaMessage)
	message := sarama.StringEncoder(jsonMessage)

	o.producer.Input() <- &sarama.ProducerMessage{
		Topic: o.config.topic,
		Value: message,
	}

	return len(message), nil
}
예제 #5
0
파일: tcp_message.go 프로젝트: MStoykov/gor
// Size returns total size of message
func (t *TCPMessage) Size() (size int) {
	size += len(proto.Body(t.packets[0].Data))

	for _, p := range t.packets[1:] {
		size += len(p.Data)
	}

	return
}
예제 #6
0
파일: tcp_message.go 프로젝트: npk/gor
// isMultipart returns true if message contains from multiple tcp packets
func (t *TCPMessage) isMultipart() bool {
	if len(t.packets) > 1 {
		return true
	}

	payload := t.packets[0].Data
	m := payload[:4]

	if t.IsIncoming {
		// If one GET, OPTIONS, or HEAD request
		if bytes.Equal(m, []byte("GET ")) || bytes.Equal(m, []byte("OPTI")) || bytes.Equal(m, []byte("HEAD")) {
			return false
		} else {
			// Sometimes header comes after the body :(
			if bytes.Equal(m, []byte("POST")) || bytes.Equal(m, []byte("PUT ")) || bytes.Equal(m, []byte("PATC")) {
				if length := proto.Header(payload, []byte("Content-Length")); len(length) > 0 {
					l, _ := strconv.Atoi(string(length))

					// If content-length equal current body length
					if l > 0 && l == len(proto.Body(payload)) {
						return false
					}
				}
			}
		}
	} else {
		if length := proto.Header(payload, []byte("Content-Length")); len(length) > 0 {
			if length[0] == '0' {
				return false
			}

			l, _ := strconv.Atoi(string(length))

			// If content-length equal current body length
			if l > 0 && l == len(proto.Body(payload)) {
				return false
			}
		}
	}

	return true
}
예제 #7
0
// Size returns total body size
func (t *TCPMessage) BodySize() (size int) {
	if len(t.packets) == 0 || t.headerPacket == -1 {
		return 0
	}

	size += len(proto.Body(t.packets[t.headerPacket].Data))

	for _, p := range t.packets[t.headerPacket+1:] {
		size += len(p.Data)
	}

	return
}
예제 #8
0
파일: http_client.go 프로젝트: buger/gor
func (c *HTTPClient) Send(data []byte) (response []byte, err error) {
	// Don't exit on panic
	defer func() {
		if r := recover(); r != nil {
			Debug("[HTTPClient]", r, string(data))

			if _, ok := r.(error); !ok {
				log.Println("[HTTPClient] Failed to send request: ", string(data))
				log.Println("PANIC: pkg:", r, debug.Stack())
			}
		}
	}()

	if c.conn == nil || !c.isAlive() {
		Debug("[HTTPClient] Connecting:", c.baseURL)
		if err = c.Connect(); err != nil {
			log.Println("[HTTPClient] Connection error:", err)
			response = errorPayload(HTTP_CONNECTION_ERROR)
			return
		}
	}

	timeout := time.Now().Add(c.config.Timeout)

	c.conn.SetWriteDeadline(timeout)

	if !c.config.OriginalHost {
		data = proto.SetHost(data, []byte(c.baseURL), []byte(c.host))
	}

	if c.auth != "" {
		data = proto.SetHeader(data, []byte("Authorization"), []byte(c.auth))
	}

	if c.config.Debug {
		Debug("[HTTPClient] Sending:", string(data))
	}

	if _, err = c.conn.Write(data); err != nil {
		Debug("[HTTPClient] Write error:", err, c.baseURL)
		response = errorPayload(HTTP_TIMEOUT)
		return
	}

	var readBytes, n int
	var currentChunk []byte
	timeout = time.Now().Add(c.config.Timeout)
	chunked := false
	contentLength := -1
	currentContentLength := 0
	chunks := 0

	for {
		c.conn.SetReadDeadline(timeout)

		if readBytes < len(c.respBuf) {
			n, err = c.conn.Read(c.respBuf[readBytes:])
			readBytes += n
			chunks++

			if err != nil {
				if err == io.EOF {
					err = nil
				}
				break
			}

			// First chunk
			if chunked || contentLength != -1 {
				currentContentLength += n
			} else {
				// If headers are finished
				if bytes.Contains(c.respBuf[:readBytes], proto.EmptyLine) {
					if bytes.Equal(proto.Header(c.respBuf, []byte("Transfer-Encoding")), []byte("chunked")) {
						chunked = true
					} else {
						status, _ := strconv.Atoi(string(proto.Status(c.respBuf)))
						if (status >= 100 && status < 200) || status == 204 || status == 304 {
							contentLength = 0
						} else {
							l := proto.Header(c.respBuf, []byte("Content-Length"))
							if len(l) > 0 {
								contentLength, _ = strconv.Atoi(string(l))
							}
						}
					}

					currentContentLength += len(proto.Body(c.respBuf[:readBytes]))
				}
			}

			if chunked {
				// Check if chunked message finished
				if bytes.HasSuffix(c.respBuf[:readBytes], chunkedSuffix) {
					break
				}
			} else if contentLength != -1 {
				if currentContentLength > contentLength {
					Debug("[HTTPClient] disconnected, wrong length", currentContentLength, contentLength)
					c.Disconnect()
					break
				} else if currentContentLength == contentLength {
					break
				}
			}
		} else {
			if currentChunk == nil {
				currentChunk = make([]byte, readChunkSize)
			}

			n, err = c.conn.Read(currentChunk)

			if err == io.EOF {
				break
			} else if err != nil {
				Debug("[HTTPClient] Read the whole body error:", err, c.baseURL)
				break
			}

			readBytes += int(n)
			chunks++
			currentContentLength += n

			if chunked {
				// Check if chunked message finished
				if bytes.HasSuffix(currentChunk[:n], chunkedSuffix) {
					break
				}
			} else if contentLength != -1 {
				if currentContentLength > contentLength {
					Debug("[HTTPClient] disconnected, wrong length", currentContentLength, contentLength)
					c.Disconnect()
					break
				} else if currentContentLength == contentLength {
					break
				}
			} else {
				Debug("[HTTPClient] disconnected, can't find Content-Length or Chunked")
				c.Disconnect()
				break
			}
		}

		if readBytes >= maxResponseSize {
			Debug("[HTTPClient] Body is more than the max size", maxResponseSize,
				c.baseURL)
			break
		}

		// For following chunks expect less timeout
		timeout = time.Now().Add(c.config.Timeout / 5)
	}

	if err != nil {
		Debug("[HTTPClient] Response read error", err, c.conn, readBytes)
		response = errorPayload(HTTP_TIMEOUT)
		return
	}

	if readBytes > len(c.respBuf) {
		readBytes = len(c.respBuf)
	}
	payload := make([]byte, readBytes)
	copy(payload, c.respBuf[:readBytes])

	if c.config.Debug {
		Debug("[HTTPClient] Received:", string(payload))
	}

	if c.config.FollowRedirects > 0 && c.redirectsCount < c.config.FollowRedirects {
		status := payload[9:12]

		// 3xx requests
		if status[0] == '3' {
			c.redirectsCount++

			location := proto.Header(payload, []byte("Location"))
			redirectPayload := []byte("GET " + string(location) + " HTTP/1.1\r\n\r\n")

			if c.config.Debug {
				Debug("[HTTPClient] Redirecting to: " + string(location))
			}

			return c.Send(redirectPayload)
		}
	}

	if bytes.Equal(proto.Status(payload), []byte("400")) {
		c.Disconnect()
		Debug("[HTTPClient] Closed connection on 400 response")
	}

	c.redirectsCount = 0

	return payload, err
}
예제 #9
0
파일: middleware_test.go 프로젝트: npk/gor
func TestTokenMiddleware(t *testing.T) {
	var resp, token []byte

	wg := new(sync.WaitGroup)

	from := NewFakeSecureService(wg, func(path string, status int, tok []byte) {
		time.Sleep(10 * time.Millisecond)
	})
	defer from.Close()

	to := NewFakeSecureService(wg, func(path string, status int, tok []byte) {
		switch path {
		case "/secure":
			if status != 202 {
				t.Error("Server should receive valid rewritten token")
			}
		}

		time.Sleep(10 * time.Millisecond)
	})
	defer to.Close()

	quit := make(chan int)

	Settings.middleware = "go run ./examples/middleware/token_modifier.go"

	fromAddr := strings.Replace(from.Listener.Addr().String(), "[::]", "127.0.0.1", -1)
	// Catch traffic from one service
	input := NewRAWInput(fromAddr, testRawExpire)
	defer input.Close()

	// And redirect to another
	output := NewHTTPOutput(to.URL, &HTTPOutputConfig{Debug: true})

	Plugins.Inputs = []io.Reader{input}
	Plugins.Outputs = []io.Writer{output}

	// Start Gor
	go Start(quit)

	// Wait for middleware to initialize
	// Give go compiller time to build programm
	time.Sleep(500 * time.Millisecond)

	// Should receive 2 requests from original + 2 from replayed
	wg.Add(4)

	client := NewHTTPClient(from.URL, &HTTPClientConfig{Debug: false})

	// Sending traffic to original service
	resp, _ = client.Get("/token")
	token = proto.Body(resp)

	// When delay is too smal, middleware does not always rewrite requests in time
	// Hopefuly client will have delay more then 10ms :)
	time.Sleep(10 * time.Millisecond)

	resp, _ = client.Get("/secure?token=" + string(token))
	if !bytes.Equal(proto.Status(resp), []byte("202")) {
		t.Error("Valid token should return 202:", proto.Status(resp))
	}

	wg.Wait()
	close(quit)
	time.Sleep(100 * time.Millisecond)
	Settings.middleware = ""
}