python」タグアーカイブ

BrokenPipeError: [Errno 32] Broken pipe調査

ESP8266(ESP-WROOM-02)の内容。ESP-WROOM-32ではないので注意。

事象

esp8266 -> nginx(proxy) -> python(HTTPServer)という構成でサーバへespからサーバーへリクエストを投げたところ、かなりの確率で以下のログがpythonで吐かれていた。

Exception occurred during processing of request from ('127.0.0.1', 48024)
Traceback (most recent call last):
  File "/usr/lib/python3.10/socketserver.py", line 316, in _handle_request_noblock
    self.process_request(request, client_address)
  File "/usr/lib/python3.10/socketserver.py", line 347, in process_request
    self.finish_request(request, client_address)
  File "/usr/lib/python3.10/socketserver.py", line 360, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "/usr/lib/python3.10/socketserver.py", line 747, in __init__
    self.handle()
  File "/usr/lib/python3.10/http/server.py", line 433, in handle
    self.handle_one_request()
  File "/usr/lib/python3.10/http/server.py", line 421, in handle_one_request
    method()
  File "/home/yoshitake/api/mail_api.py", line 26, in do_GET
    self.wfile.write(response_content)
  File "/usr/lib/python3.10/socketserver.py", line 826, in write
    self._sock.sendall(b)
BrokenPipeError: [Errno 32] Broken pipe

直接のメッセージは、pythonがリクエスト元に書きこもうとしたら相手がすでに受信待ち状態を終了しているような場合に起きるやつ。esp側では接続したもののヘッダーも取得せずにすぐに終了している。

ちなみにnginxのaccess.logには以下のようにステータス499と(pythonのHTTPServer他で使われている非標準の)クライアントが先に閉じたよというのが残っている。

150.9.94.220 - - [26/Feb/2024:11:31:56 +0900] "GET /api/reserve_count HTTP/1.0" 499 0 "-" "ESP8266"

直接の原因箇所

リクエストを送るesp側でデータの受信待ちをしなくてすぐに閉じているのが直接の原因

delay(1000);とかを入れたらとりあえず動く。

とはいえ、リクエストを送ったのちにデータ受信後の終了信号的なものをみたらまたはタイムアウトでクローズの方がよさそう。

また、同じ接続方式でself-signed.badssl.comにリクエストを送るとレスポンスがきちんと取得できているのでサーバサイドにも何か改善点がありそう。

根本解決

クライアントサイド(ESP8266)

取り急ぎavailable()では接続できたかしか確認していない。 WiFiServerSecureBearSSL.cpp(サーバの方)を間違えてみていた。

改めてWiFiClientSecureBearSSL.cpp確認。

字面で見る限りなんか入ってたら0以外が入って、なにも入っていないとサーバ側が準備中でも一番下に入って0がくるっぽい。だいぶ中の方に書いてあるので内部データを見ると色々多変そうなので最初のデータ来るかタイムアウトまでの待ち処理を入れるのがよさそう。断続的にデータがくるならちゃんと終端処理を見た方がよいかも。

本体から、書き出しメソッド呼び出し。

サーバサイド(nginx+pythonのHTTPServer)

こちらは単純にレスポンスが早く返りだすかどうかで違いが出ていたっぽい。レスポンスが早いという理由だった場合はクライアント側の処理で解決すべき話なので深く追わない。

pythonのimport imaplibで日本語フォルダ名を使う場合はエンコードが必要

環境

  • PRETTY_NAME=”Ubuntu 22.04.3 LTS”
  • Python 3.10.12
  • pip 22.0.2 from /usr/lib/python3/dist-packages/pip (python 3.10)

手順

imap_toolsというのが楽らしいのでインストール

pip install imap_tools

インポートして実行

import imap_tools
mailbox_name = "INBOX.ジム.予約"
status, messages = mail.select(imap_tools.imap_utf7.encode(mailbox_name))

何もしない場合のエラー

mail.select(“INBOX.ジム.予約”)

として

yoshitake@mail:~/dovecot_delivery$ python3 test.py 
Traceback (most recent call last):
  File "/home/yoshitake/dovecot_delivery/test.py", line 15, in <module>
    mail.select("INBOX.ジム.予約")
  File "/usr/lib/python3.10/imaplib.py", line 756, in select
    typ, dat = self._simple_command(name, mailbox)
  File "/usr/lib/python3.10/imaplib.py", line 1230, in _simple_command
    return self._command_complete(name, self._command(name, *args))
  File "/usr/lib/python3.10/imaplib.py", line 987, in _command
    arg = bytes(arg, self._encoding)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 6-7: ordinal not in range(128)

ソース全体

import imaplib
import imap_tools
from email.header import decode_header

# IMAPサーバの設定
mail_server = 'mail.huga.co.jp'
username = 'hoge'
password = 'hugahuga'

# IMAPサーバに接続
mail = imaplib.IMAP4_SSL(mail_server)
mail.login(username, password)

# # メールボックスを選択
mailbox_name = "INBOX.ジム.予約"
status, messages = mail.select(imap_tools.imap_utf7.encode(mailbox_name))

# # メール検索(例: 未読メール)
status, messages = mail.search(None, "(UNSEEN)")
unread_count = len(messages[0].split())
print(f"未読メールの件数: {unread_count}")

# 接続を閉じる
mail.close()
mail.logout()