Exemple #1
0
// mainLoop initiates all ports and handles the traffic
func mainLoop() {
	openPorts()
	defer closePorts()

	waitCh := make(chan bool)
	go func() {
		total := 0
		for {
			v := <-outCh
			if !v {
				log.Println("An OUT port is closed. Interrupting execution")
				exitCh <- syscall.SIGTERM
				break
			} else {
				total++
			}
			// At least one output ports are opened
			if total >= 1 && waitCh != nil {
				waitCh <- true
			}
		}
	}()

	log.Println("Waiting for port connections to establish... ")
	select {
	case <-waitCh:
		log.Println("Ports connected")
		waitCh = nil
	case <-time.Tick(30 * time.Second):
		log.Println("Timeout: port connections were not established within provided interval")
		os.Exit(1)
	}

	// Setup socket poll items
	poller := zmq.NewPoller()
	poller.Add(intPort, zmq.POLLIN)
	poller.Add(reqPort, zmq.POLLIN)
	poller.Add(tmplPort, zmq.POLLIN)

	// This is obviously dangerous but we need it to deal with our custom CA's
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}
	client.Timeout = 30 * time.Second

	var (
		interval     time.Duration
		ip           [][]byte
		request      *requestIP
		propTemplate *caf.PropertyTemplate
		httpRequest  *http.Request
	)

	for {
		sockets, err := poller.Poll(-1)
		if err != nil {
			log.Println("Error polling ports:", err.Error())
			continue
		}
		for _, socket := range sockets {
			if socket.Socket == nil {
				log.Println("ERROR: could not find socket in polling items array")
				continue
			}
			ip, err = socket.Socket.RecvMessageBytes(0)
			if err != nil {
				log.Println("Error receiving message:", err.Error())
				continue
			}
			if !runtime.IsValidIP(ip) || !runtime.IsPacket(ip) {
				log.Println("Invalid IP:", ip)
				continue
			}
			switch socket.Socket {
			case intPort:
				interval, err = time.ParseDuration(string(ip[1]))
				log.Println("Interval specified:", interval)
			case reqPort:
				err = json.Unmarshal(ip[1], &request)
				if err != nil {
					log.Println("ERROR: failed to unmarshal request:", err.Error())
					continue
				}
				log.Println("Request specified:", request)
			case tmplPort:
				err = json.Unmarshal(ip[1], &propTemplate)
				if err != nil {
					log.Println("ERROR: failed to unmarshal template:", err.Error())
					continue
				}
				log.Printf("Template specified: %+v", propTemplate)

			default:
				log.Println("ERROR: IP from unhandled socket received!")
				continue
			}
		}
		if interval > 0 && request != nil && propTemplate != nil {
			log.Println("Component configured. Moving on...")
			break
		}
	}

	log.Println("Started...")
	ticker := time.NewTicker(interval)
	for _ = range ticker.C {
		httpRequest, err = http.NewRequest(request.Method, request.URL, nil)
		utils.AssertError(err)

		// Set the accepted Content-Type
		if request.ContentType != "" {
			httpRequest.Header.Add("Content-Type", request.ContentType)
		}

		// Set any additional headers if provided
		for k, v := range request.Headers {
			httpRequest.Header.Add(k, v[0])
		}

		response, err := client.Do(httpRequest)
		if err != nil {
			log.Printf("ERROR performing HTTP %s %s: %s\n", request.Method, request.URL, err.Error())
			if errPort != nil {
				errPort.SendMessageDontwait(runtime.NewPacket([]byte(err.Error())))
			}
			continue
		}

		resp, err := httputils.Response2Response(response)
		if err != nil {
			log.Println("ERROR converting response to reply:", err.Error())
			if errPort != nil {
				errPort.SendMessageDontwait(runtime.NewPacket([]byte(err.Error())))
			}
			continue
		}

		// Property output socket
		if propPort != nil {
			var data interface{}
			if strings.HasSuffix(request.ContentType, "json") {
				err = json.Unmarshal(resp.Body, &data)
				if err != nil {
					log.Println("ERROR unmarshaling the JSON response:", err.Error())
					continue
				}
			} else {
				// TODO: support other content-types
				log.Printf("WARNING processing of %s is not supported", request.ContentType)
				continue
			}

			prop, err := propTemplate.Fill(data)
			if err != nil {
				log.Println("ERROR filling template with data: ", err.Error())
				continue
			}

			out, _ := json.Marshal(prop)
			propPort.SendMessage(runtime.NewPacket(out))
		}

		// Extra output sockets (e.g., for debugging)
		if respPort != nil {
			ip, err = httputils.Response2IP(resp)
			if err != nil {
				log.Println("ERROR converting reply to IP:", err.Error())
				if errPort != nil {
					errPort.SendMessageDontwait(runtime.NewPacket([]byte(err.Error())))
				}
			} else {
				respPort.SendMessage(ip)
			}
		}
		if bodyPort != nil {
			bodyPort.SendMessage(runtime.NewPacket(resp.Body))
		}
	}
}
Exemple #2
0
// mainLoop initiates all ports and handles the traffic
func mainLoop() {
	openPorts()
	defer closePorts()

	ports := 1
	if bodyPort != nil {
		ports++
	}
	if respPort != nil {
		ports++
	}
	if errPort != nil {
		ports++
	}

	waitCh := make(chan bool)
	reqExitCh := make(chan bool, 1)
	go func(num int) {
		total := 0
		for {
			select {
			case v := <-reqCh:
				if v {
					total++
				} else {
					reqExitCh <- true
				}
			case v := <-bodyCh:
				if !v {
					log.Println("BODY port is closed. Interrupting execution")
					exitCh <- syscall.SIGTERM
					break
				} else {
					total++
				}
			case v := <-respCh:
				if !v {
					log.Println("RESP port is closed. Interrupting execution")
					exitCh <- syscall.SIGTERM
					break
				} else {
					total++
				}
			case v := <-errCh:
				if !v {
					log.Println("ERR port is closed. Interrupting execution")
					exitCh <- syscall.SIGTERM
					break
				} else {
					total++
				}
			}
			if total >= num && waitCh != nil {
				waitCh <- true
			}
		}
	}(ports)

	log.Println("Waiting for port connections to establish... ")
	select {
	case <-waitCh:
		log.Println("Ports connected")
		waitCh = nil
	case <-time.Tick(30 * time.Second):
		log.Println("Timeout: port connections were not established within provided interval")
		exitCh <- syscall.SIGTERM
		return
	}

	// This is obviously dangerous but we need it to deal with our custom CA's
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}
	client.Timeout = 30 * time.Second

	// Main loop
	var (
		ip            [][]byte
		clientOptions *httputils.HTTPClientOptions
		request       *http.Request
	)

	log.Println("Started")

	for {
		ip, err = reqPort.RecvMessageBytes(zmq.DONTWAIT)
		if err != nil {
			select {
			case <-reqExitCh:
				log.Println("REQ port is closed. Interrupting execution")
				exitCh <- syscall.SIGTERM
				break
			default:
				// IN port is still open
			}
			time.Sleep(2 * time.Second)
			continue
		}
		if !runtime.IsValidIP(ip) {
			log.Println("Invalid IP:", ip)
			continue
		}

		err = json.Unmarshal(ip[1], &clientOptions)
		if err != nil {
			log.Println("ERROR: failed to unmarshal request options:", err.Error())
			continue
		}
		if clientOptions == nil {
			log.Println("ERROR: received nil request options")
			continue
		}

		if clientOptions.Form != nil {
			request, err = http.NewRequest(clientOptions.Method, clientOptions.URL, strings.NewReader(clientOptions.Form.Encode()))
		} else {
			request, err = http.NewRequest(clientOptions.Method, clientOptions.URL, nil)
		}
		utils.AssertError(err)

		if clientOptions.ContentType != "" {
			request.Header.Add("Content-Type", clientOptions.ContentType)
		}

		for k, v := range clientOptions.Headers {
			request.Header.Add(k, v[0])
		}

		response, err := client.Do(request)
		if err != nil {
			log.Printf("ERROR performing HTTP %s %s: %s", request.Method, request.URL, err.Error())
			if errPort != nil {
				errPort.SendMessageDontwait(runtime.NewPacket([]byte(err.Error())))
			}
			clientOptions = nil
			continue
		}
		resp, err := httputils.Response2Response(response)
		if err != nil {
			log.Printf("ERROR converting response to reply: %s", err.Error())
			if errPort != nil {
				errPort.SendMessageDontwait(runtime.NewPacket([]byte(err.Error())))
			}
			clientOptions = nil
			continue
		}
		ip, err = httputils.Response2IP(resp)
		if err != nil {
			log.Printf("ERROR converting reply to IP: %s", err.Error())
			if errPort != nil {
				errPort.SendMessageDontwait(runtime.NewPacket([]byte(err.Error())))
			}
			clientOptions = nil
			continue
		}

		if respPort != nil {
			respPort.SendMessage(ip)
		}
		if bodyPort != nil {
			bodyPort.SendMessage(runtime.NewPacket(resp.Body))
		}

		select {
		case <-reqCh:
			log.Println("REQ port is closed. Interrupting execution")
			exitCh <- syscall.SIGTERM
			break
		default:
			// file port is still open
		}

		clientOptions = nil
		continue
	}
}
Exemple #3
0
func main() {
	flag.Parse()

	if *jsonFlag {
		doc, _ := registryEntry.JSON()
		fmt.Println(string(doc))
		os.Exit(0)
	}

	log.SetFlags(0)
	if *debug {
		log.SetOutput(os.Stdout)
	} else {
		log.SetOutput(ioutil.Discard)
	}

	validateArgs()

	openPorts()
	defer closePorts()

	poller.Add(requestPort, zmq.POLLIN)

	exitCh := utils.HandleInterruption()
	err = runtime.SetupShutdownByDisconnect(requestPort, "http-router.in", exitCh)
	utils.AssertError(err)

	// Main loop
	var (
		index       int = -1
		outputIndex int = -1
		params      url.Values
		ip          [][]byte
		pLength     int     = len(pollItems)
		router      *Router = NewRouter()
	)
	for {
		// Poll sockets
		log.Println("Polling sockets...")

		sockets, err := poller.Poll(-1)
		if err != nil {
			log.Println("Error polling ports:", err.Error())
			os.Exit(1)
		}

		for index, s := range sockets {
			ip, err = s.Socket.RecvMessageBytes(0)
			if err != nil {
				log.Printf("Failed to receive data. Error: %s", err.Error())
				continue
			}
			if !runtime.IsValidIP(ip) {
				log.Println("Received invalid IP")
				continue
			}
		}

		// Pattern arrived

		if index < pLength-1 {
			// Close pattern socket
			port = pollItems[index].Socket
			port.Close()

			// Resolve corresponding output socket index
			outputIndex = -1
			for i, s := range patternPorts {
				if s == port {
					outputIndex = i
				}
			}
			if outputIndex == -1 {
				log.Printf("Failed to resolve output socket index")
				continue
			}

			// Remove closed socket from polling items
			pollItems = append(pollItems[:index], pollItems[index+1:]...)
			pLength -= 1

			// Add pattern to router
			parts := strings.Split(string(ip[1]), " ")
			method := strings.ToUpper(strings.TrimSpace(parts[0]))
			pattern := strings.TrimSpace(parts[1])
			switch method {
			case "GET":
				router.Get(pattern, outputIndex)
			case "POST":
				router.Post(pattern, outputIndex)
			case "PUT":
				router.Put(pattern, outputIndex)
			case "DELETE":
				router.Del(pattern, outputIndex)
			case "HEAD":
				router.Head(pattern, outputIndex)
			case "OPTIONS":
				router.Options(pattern, outputIndex)
			default:
				log.Printf("Unsupported HTTP method %s in pattern %s", method, pattern)
			}
			continue
		}

		// Request arrive
		req, err := httputils.IP2Request(ip)
		if err != nil {
			log.Printf("Failed to convert IP to request. Error: %s", err.Error())
			continue
		}

		outputIndex, params = router.Route(req.Method, req.URI)
		log.Printf("Output index for %s %s: %v (params=%#v)", req.Method, req.URI, outputIndex, params)

		switch outputIndex {
		case NotFound:
			log.Println("Sending Not Found response to FAIL output")
			resp := &httputils.HTTPResponse{
				Id:         req.Id,
				StatusCode: http.StatusNotFound,
			}
			ip, _ = httputils.Response2IP(resp)
			failPort.SendMultipart(ip, 0)
		case MethodNotAllowed:
			log.Println("Sending Method Not Allowed response to FAIL output")
			resp := &httputils.HTTPResponse{
				Id:         req.Id,
				StatusCode: http.StatusMethodNotAllowed,
			}
			ip, _ = httputils.Response2IP(resp)
			failPort.SendMultipart(ip, 0)
		default:
			for k, values := range params {
				req.Form[k] = values
			}
			ip, _ = httputils.Request2IP(req)
			successPorts[outputIndex].SendMultipart(ip, 0)
		}

		index = -1
	}
}