簡體   English   中英

從espduino超時向Flask發布

[英]POST to Flask from espduino timing out

我正在玩esp8266 ,通過這個庫為Arduino提供WiFi。 我把它設置為POST到Pushover以及requestbin ,在調試輸出和這些工具的接口之間,我可以驗證請求數據是否正確發布,並且esp8266 / arduino單元的響應狀態代碼正確顯示200。

我想測試設置的可靠性,所以我想我會轉向Flask。 我將來自espduino的100個請求發送到for循環到運行在0.0.0.0:5000的Flask應用程序。 POST包含一個測試字符串(試圖粗略地衡量簡單的數據完整性,確保字符串未被破壞)以及正在發送的循環數(例如,第一個循環為0 ...最后一個為99) 。 Flask會跟蹤這些請求並更新其輸出,以顯示哪些請求正確通過,哪些請求未通過。

設置工作正常,除了一件事:模塊在每個請求后超時。 每個POST似乎都有效,Flask會相應地更新輸出,但espduino會為每個請求花費整整5秒(默認超時),並說它的響應代碼為0

所以重申一下情況:我的espduino設置正確POST並從requestb.in和pushover.net獲得200響應,但是我的本地Flask服務器POST然后超時。

為什么

  • 確保Flask讀取所有表單數據 - >沒有區別
  • 與gunicorn而不是內置的Flask服務器一起服務 - >沒有區別
  • 將響應內容類型明確更改為“text / html” - >沒有區別
  • 將響應內容類型更改為“application / json” - >沒有區別
  • 添加; charset=utf-8 ; charset=utf-8到內容類型 - >沒有區別
  • 將Flask api端點從index /更改為/api - >沒有區別
  • 改變端口 - >沒有區別
  • 驗證防火牆已關閉(OS X 10.10.5托管Flask應用程序) - >沒有區別
  • nc -l 5000並檢查來自模塊的傳入帖子(看起來不錯)
  • 從郵遞員發布並卷曲到Flask應用程序以檢查響應(並與Requestb.in /工作的站點的響應進行比較)
  • 從test_string中刪除了特殊字符
  • WSGIRequestHandler.protocol_version更改為HTTP/1.1 - >沒有區別

我花了幾天時間研究這個問題並沒有取得多大進展 我今天取得的最大突破是,如果我將我的gunicorn / Flask設置放在nginx之后,我可以成功發布並獲得200個響應,而不會更改espduino代碼,所以我確定Flask有或沒有做什么(我擔心它可能是espduino處理IP地址與域名的關系,但我認為這可以解決這個問題)。

我嘗試過的設置摘要:

  • POST到requestb.in - > POST工作,200響應,沒有超時
  • POST到pushover.net - > POST工作,200響應,沒有超時
  • POST到本地Flask服務器 - > POST工作, 沒有響應,超時
  • GET到本地Flask服務器 - > 沒有響應,超時 (沒有測試過GETs數據)
  • POST到本地gunicorn服務Flask應用程序 - > POST工作, 沒有響應,超時
  • POST到本地nginx服務gunicorn / Flask - > POST工作,200響應,沒有超時
  • POST到本地gunicorn服務httpbin - > 沒有回應,超時
  • POST到本地cherrypy服務器 - > 沒有響應,超時

當前代碼:

from flask import Flask, request, make_response
from datetime import datetime

app = Flask(__name__)

ESPDUINO_IP = 'XXX.XXX.XXX.XXX'
INCOMING_TEST_STRING = 'This is my espduino test string!'
incoming_requests = []

with open('results.txt', 'w') as f:
    f.write("Test run start: {}\n".format(datetime.now()))

@app.route('/api', methods=['GET', 'POST'])
def count_requests():
    if request.method == 'POST':
        form = request.form
        incoming_ip = request.remote_addr
        if incoming_ip == ESPDUINO_IP and form['test_string'] == INCOMING_TEST_STRING:
            test_num = int(form['test_num'])
            incoming_requests.append(test_num)
            msg = "All is peachy!"
            with open('results.txt', 'a') as f:
                f.write("{:02d}: {}\n".format(test_num, datetime.now()))
    else:
        msg = "Hey, you're not the espduino!<br>"
        msg += str(len(incoming_requests)) + " requests so far.<br>"
        missing = set(range(100)) - set(incoming_requests)
        msg += "Missing: {}<br>".format(', '.join(map(str, missing)) if missing else "None!")
        msg += '<br>'.join(map(str, incoming_requests))

    resp = make_response('{"this": "that"}')
    resp.headers['Content-Type'] = "application/json"
    return resp
    # return "<html><body>{}</body></html>".format(msg)

if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True)

這是espduino的POST看起來像:

$ nc -l 5000
POST /api HTTP/1.1
Host: XXX.XXX.XXX.XXX
Content-Length: 55
Connection: close
Content-Type: application/x-www-form-urlencoded
User-Agent: ESPDRUINO@tuanpm

test_string=This is my espduino test string!&test_num=0

curl -X POST -d 'test_string=This is my espduino test string!&test_num=0' localhost:5000/api相比curl -X POST -d 'test_string=This is my espduino test string!&test_num=0' localhost:5000/api

$ nc -l 5000
POST /api HTTP/1.1
Host: localhost:5000
User-Agent: curl/7.43.0
Accept: */*
Content-Length: 55
Content-Type: application/x-www-form-urlencoded

test_string=This is my espduino test string!&test_num=0

很想聽聽有關可能發生的事情的任何想法。 我想知道這可能是一個WSGI問題嗎?

2015年8月31日更新:

我仍然沒有想到這一點,但這絕對不是Flask特有的問題。 正如我上面提到的,我還使用CherryPy以及python3 -m http.server --bind 0.0.0.0 5000 (以及將espduino代碼更改為GET / )以及ruby -run -e httpd復制了超時。 ruby -run -e httpd 我仍然不明白為什么nginx,requestbin等服務它沒有問題。

為了回應@ Miguel關於HOST標頭沒有端口的評論,我正在努力分支並構建固件來改變這一點,但與此同時我將客戶端主機和端口硬編碼為一個小的HTTP服務器腳本,沒有運氣。

from http.server import BaseHTTPRequestHandler, HTTPServer

class MyServer(BaseHTTPRequestHandler):
    # protocol_version = 'HTTP/1.1'
    # close_connection = True
    def _set_headers(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()

    def do_GET(self):
        self._set_headers()
        self.wfile.write(b"<html><body><h1>hi!</h1></body></html>")

    def do_HEAD(self):
        self._set_headers()

    def do_POST(self):
        self.client_address = ("192.168.0.4", 5000)
        self._set_headers()
        self.wfile.write(b"<html><body><h1>POST!</h1></body></html>")
        # import pdb; pdb.set_trace()

def run(server_class=HTTPServer, handler_class=MyServer, port=5000):
    server_address = ('0.0.0.0', port)
    httpd = server_class(server_address, handler_class)
    print('Starting httpd...')
    httpd.serve_forever()

if __name__ == "__main__":
    run()

查看tcpdump,看看我是否能找到工作(nginx)和非工作網絡數據之間的任何區別。 到目前為止還沒有找到任何東西,但我也是這個工具的新手。

2015年9月8日更新

仍然沒有想到這一點,但看起來tcpdump在nginx和Python服務器之間存在顯着差異。 這是一個示例POST和響應 - 為了清楚起見,我已經用ESPDUINO_IPOSX_IP替換了IP,並清理了周圍的ACK調用等。 我需要調查為什么Python響應被這條奇怪的行中斷 - 我檢查了10個連續的POST / Response對,並且每個 Python響應都被中斷了(在完全相同的地方),並且沒有 nginx響應是,所以我想知道這可能是問題。 (另外,正如您所看到的,在本輪測試中,我已將響應主體更改為文本而不是JSON - 結果沒有變化。)

nginx(作品)
 POST /api HTTP/1.1 Host: OSX_IP Content-Length: 29 Connection: close Content-Type: application/x-www-form-urlencoded; charset=utf-8 User-Agent: espduino@n8henrie test_string=simple&test_num=0 09:16:04.079291 IP OSX_IP.commplex-main > ESPDUINO_IP.49146: Flags [P.], seq 1:183, ack 211, win 65535, length 182 HTTP/1.1 200 OK Server: nginx/1.8.0 Date: Mon, 31 Aug 2015 15:16:04 GMT Content-Type: text/html; charset=utf-8 Content-Length: 26 Connection: close <html><body></body></html> 
燒瓶(超時)
 POST /api HTTP/1.1 Host: OSX_IP Content-Length: 29 Connection: close Content-Type: application/x-www-form-urlencoded; charset=utf-8 User-Agent: espduino@n8henrie test_string=simple&test_num=3 09:00:19.424086 IP OSX_IP.commplex-main > ESPDUINO_IP.48931: Flags [P.], seq 1:18, ack 211, win 65535, length 17 HTTP/1.0 200 OK 09:00:36.382125 IP OSX_IP.commplex-main > ESPDUINO_IP.48931: Flags [FP.], seq 18:181, ack 211, win 65535, length 163 E....F@.@..,...e.......#...k..SP......Content-Type: text/html; charset=utf-8 Content-Length: 26 Server: Werkzeug/0.10.4 Python/3.4.3 Date: Mon, 31 Aug 2015 15:00:36 GMT <html><body></body></html> 

我認為Python就像某種原因將響應分成兩半,例如length 17一部分和length 17length 163另一部分,與nginx的length 182的單一響應相比。

2015年9月10日更新

有趣的是,如果我通過mitmproxy運行它,一切都按預期工作 - 甚至直接到沒有nginx或gunicorn的Flask應用程序。 一旦我刪除了mitmproxy,它就會回到上面的超時時間。

仍然沒有解決問題,但我想我可能已經弄明白是什么導致了它。 畢竟不是Flask問題。

不幸的是,這似乎是esp_bridge庫的一個錯誤,其espduino固件使用esp8266。 請原諒可能不正確的術語,但是由於某種原因,我可以告訴它似乎並沒有加入TCP數據包。 產生分解為單獨TCP數據包(例如Flask)的HTTP響應的服務器失敗,而tcpdump可以驗證nginx和mitmproxy正在加入拆分TCP數據包並在單個數據包中返回響應,這就是他們正在工作的原因。

https://github.com/tuanpmt/esp_bridge/issues/10

更新20160128

我今天重新審視了這個問題並找到了解決方法 雖然理想的解決方案是修復esp_bridge以重新組合多數據包響應,但只要響應非常小,就可以強制Flask在單個數據包中寫入響應。

from werkzeug.serving import WSGIRequestHandler

# Your Flask code here...

if __name__ == "__main__":
    WSGIRequestHandler.wbufsize = -1
    app.run()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM