1 Error phenomena
The HTTP service implemented by Threadinghttpserver, if the client is actively disconnected before the server returns, the server side will report [Errno] broken pipe wrong and cause the processing thread to crash.
Let's take a look at an example, Python version: 2.7
Sample code
The code is as follows |
Copy Code |
#!/usr/bin/env python #!coding=utf-8
Import OS Import time Import socket Import threading From Basehttpserver import Httpserver, Basehttprequesthandler From Socketserver import ThreadingMixIn
Class RequestHandler (Basehttprequesthandler): def do_get (self): """ Handling GET Requests """ Query=self.path Print "Query:%s thread=%s"% (query, str (threading.current_thread ())
#ret_str = "Ret_str= " Time.sleep (5)
Try Self.send_response (200) Self.send_header (' Content-type ', ' text/html ') Self.end_headers () Self.wfile.write (RET_STR) Except Socket.error, E: Print "Socket.error:Connection broke. Aborting "+ str (e) Self.wfile._sock.close () # Close Socket Self.wfile._sock=none Return False
Print "Success PROD query:%s"% (query) Return True
#多线程处理 Class Threadinghttpserver (Threadingmixin,httpserver): Pass
if __name__ = = ' __main__ ': Serveraddr = (', 9001)
Ser = Threadinghttpserver (Serveraddr,requesthandler) Ser.serve_forever () Sys.exit (0) Running Services ./thread_http_server_error.py |
1th time Curl, waiting to return
The code is as follows |
Copy Code |
[~] $curl-S ' http://10.232.41.142:9001/hello1′ At this point the server-side output log is as follows: $./thread_http_server_error.py Query:/hello1 thread= search041142.sqa.cm4.tbsite.net–-[15/may/2014 15:02:27] "Get/hello1 http/1.1″200- Success PROD query:/hello1 |
2nd time Curl, do not wait to return, CTRL +C to simulate client disconnect
The code is as follows |
Copy Code |
[~] $curl-S ' http://10.232.41.142:9001/hello2′ [~]$ Ctrl + C |
At this point the server-side output log is as follows:
Query:/hello2 thread=
search041142.sqa.cm4.tbsite.net–-[15/may/2014 15:33:10] "Get/hello2 http/1.1″200-
Socket.error:Connection broke. Aborting[errno] Broken Pipe
—————————————-
Exception happened during processing of request from (' 10.232.41.142′, 48769)
Traceback (most recent call last):
File "/home/wuzhu/tools/python_2_7_1/lib/python2.7/socketserver.py", line 582, in Process_request_thread
Self.finish_request (Request, client_address)
File "/home/wuzhu/tools/python_2_7_1/lib/python2.7/socketserver.py", line 323, in Finish_request
Self. Requesthandlerclass (Request, client_address, self)
File "/home/wuzhu/tools/python_2_7_1/lib/python2.7/socketserver.py", line 639, in __init__
Self.handle ()
File "/home/wuzhu/tools/python_2_7_1/lib/python2.7/basehttpserver.py", line 337, in handle
Self.handle_one_request ()
File "/home/wuzhu/tools/python_2_7_1/lib/python2.7/basehttpserver.py", line 326, in Handle_one_request
Self.wfile.flush () #actually Send the response if not already done.
File "/home/wuzhu/tools/python_2_7_1/lib/python2.7/socket.py", line 303, in flush
Self._sock.sendall (View[write_offset:write_offset+buffer_size])
Attributeerror: ' Nonetype ' object has no attribute ' Sendall '
—————————————-
2 Causal Analysis
"[Errno] broken pipe" The reason is relatively clear, because the client in the server before the return of the active disconnect, so the server on the return write socket received sigpipe error. Although exceptions are also processed in our programs, close the handler Wfile._sock object, but the member functions of the Basehttprequesthandler class in Python's library basehttpserver.py handle _one_request will still call Wfile.flush directly, without determining if wfile is close.
The code is as follows |
Copy Code |
def handle_one_request (self): "" "Handle a single HTTP request.
You are normally don ' t need to override this method; The class __doc__ string for information on I to handle specific HTTP Commands such as Get and POST.
""" Try Self.raw_requestline = Self.rfile.readline (65537) If Len (self.raw_requestline) > 65536: Self.requestline = ' ' self.request_version = ' ' Self.command = ' ' Self.send_error (414) Return If not self.raw_requestline: Self.close_connection = 1 Return If not self.parse_request (): # An error code has been sent, just exit Return Mname = ' Do_ ' + self.command If not hasattr (self, mname): Self.send_error (501, "Unsupported Method (%r)"% Self.command) Return method = GetAttr (self, mname) Method () #没有判断 Wfile is already close, call flush directly () Self.wfile.flush () #actually Send the response if not already done. Except Socket.timeout, E: #a Read or a write timed out. Discard this connection Self.log_error ("Request timed out:%r", E) Self.close_connection = 1 Return |
3 Solutions
As long as you overload the member function Handle_one_reques () of its base class Basehttprequesthandler in RequestHandler, precede the call to Wfile.flush () plus whether the wfile is close.
The code is as follows |
Copy Code |
#!/usr/bin/env python #!coding=utf-8
Import OS Import time Import socket Import threading From Basehttpserver import Httpserver, Basehttprequesthandler From Socketserver import ThreadingMixIn
Class RequestHandler (Basehttprequesthandler):
def handle_one_request (self): "" "Handle a single HTTP request.
You are normally don ' t need to override this method; The class __doc__ string for information on I to handle specific HTTP Commands such as Get and POST.
""" Try Self.raw_requestline = Self.rfile.readline (65537) If Len (self.raw_requestline) > 65536: Self.requestline = ' ' self.request_version = ' ' Self.command = ' ' Self.send_error (414) Return If not self.raw_requestline: Self.close_connection = 1 Return If not self.parse_request (): # An error code has been sent, just exit Return Mname = ' Do_ ' + self.command If not hasattr (self, mname): Self.send_error (501, "Unsupported Method (%r)"% Self.command) Return method = GetAttr (self, mname) Print "Before Call Do_get" Method () #增加 Debug Info and Wfile to determine if close Print "After call Do_get" If not self.wfile.closed: Self.wfile.flush () #actually Send the response if not already done. Print "After Wfile.flush ()" Except Socket.timeout, E: #a Read or a write timed out. Discard this connection Self.log_error ("Request timed out:%r", E) Self.close_connection = 1 Return
def do_get (self): """ Handling GET Requests """ Query=self.path Print "Query:%s thread=%s"% (query, str (threading.current_thread ())
Ret_str= " Time.sleep (5)
Try Self.send_response (200) Self.send_header (' Content-type ', ' text/html ') Self.end_headers () Self.wfile.write (RET_STR) Except Socket.error, E: Print "Socket.error:Connection broke. Aborting "+ str (e) Self.wfile._sock.close () Self.wfile._sock=none Return False
Print "Success PROD query:%s"% (query) Return True
#多线程处理 Class Threadinghttpserver (Threadingmixin,httpserver): Pass
if __name__ = = ' __main__ ': Serveraddr = (', 9001)
Ser = Threadinghttpserver (Serveraddr,requesthandler) Ser.serve_forever () Sys.exit (0) Running Services ./thread_http_server.py Curl, do not wait to return, CTRL +C to simulate client disconnect [~] $curl-S ' Http://10.232.41.142:9001/hello2 ' [~]$ Ctrl + C |
At this point the server-side output log is as follows:
The code is as follows |
Copy Code |
$./thread_http_server.pybefore Call Do_get Query:/hello2 thread=<thread (Thread-1, started 1103210816) > Search041142.sqa.cm4.tbsite.net--[15/may/2014 15:54:09] "Get/hello2 http/1.1" 200- Socket.error:Connection broke. Aborting[errno] Broken Pipe After the call Do_get After Wfile.flush () |