The previous article describes the processing of the request, the first face LIGHTTPD will return the results of processing to the client. The state machine enters the Con_state_responst_start. In this state, the main work of the server is in the function Connection_handle_write_prepare. This function is not complex, mainly based on the client request method to set the response headers, in fact, is to set the value of "Content-length". Here is the function code, which makes some limitations:
Static intConnection_handle_write_prepare (server * SRV, connection *con) { if(Con->mode = =DIRECT) { /** Static Files*/ Switch(con->Request.http_method) { CaseHttp_method_get: CaseHttp_method_post: CaseHttp_method_head: CaseHttp_method_put: CaseHttp_method_mkcol: CaseHttp_method_delete: Casehttp_method_copy: CaseHttp_method_move: CaseHttp_method_propfind: CaseHttp_method_proppatch: CaseHttp_method_lock: CaseHttp_method_unlock: Break; Casehttp_method_options:/** Coming from the Request-parser before Uri.path is set * 403 are from the response Handl Er when noone else catched it **/ if((!con->http_status | | con->http_status = = $) && con->uri.path->used && con->uri.path->ptr[0] !='*') {Response_header_insert (SRV, con, Const_str_len (" Allow"), Const_str_len ("OPTIONS, GET, HEAD, POST")); Con->response.transfer_encoding &= ~http_transfer_encoding_chunked; Con->parsed_response &= ~http_content_length; Con->http_status = $; Con->file_finished =1; Chunkqueue_reset (Con-write_queue); } Break; default: Switch(con->http_status) { Case -:/*Bad request*/ Case 414:/*Overload request Header*/ Case 505:/*Unknown Protocol*/ Case 207:/*This is WebDAV*/ Break; default: Con->http_status =501; Break; } Break; } } if(Con->http_status = =0) {con->http_status =403; } Switch(con->http_status) { Case 204:/*Class:header only*/ Case 205: Case 304: /** Disable chunked encoding again as we have no body*/Con->response.transfer_encoding &= ~http_transfer_encoding_chunked; Con->parsed_response &= ~http_content_length; Chunkqueue_reset (Con-write_queue); Con->file_finished =1; Break; default:/*Class:header + Body*/ if(Con->mode! =DIRECT) Break; /** Only custom body for 4xx and 5xx*/ if(Con->http_status < -|| Con->http_status >= -) Break; Con->file_finished =0; Buffer_reset (Con-Physical.path); /** Try to send static ErrorFile*/ if(!buffer_is_empty (con->Conf.errorfile_prefix)) { //set the corresponding error prompt file } if(!con->file_finished) { //no corresponding error message file, set default error prompt. } Break; } if(con->file_finished) { /** We have all the content and chunked encoding are not used, set a * content-length*/ if((! (Con->parsed_response & Http_content_length)) &&(Con->response.transfer_encoding & http_transfer_encoding_chunked) = =0) {off_t Qlen= Chunkqueue_length (con->write_queue); /** The content-length header is only can sent if we have Content: *-HEAD doesn ' t has a conte Nt-body (but has a content-length) *-1xx, 204 and 304 don ' t has a content-body (RFC 2616 section 4.3) * * Otherwise generate a content-length header as chunked encoding is not * available */ if((Con->http_status >= -&& Con->http_status < $) ||Con->http_status = =204|| Con->http_status = =304) {data_string*ds; /** No content-body, no content-length*/ if(NULL! = (ds = (data_string *) array_get_element (con->response.headers,"Content-length")) {Buffer_reset (ds->value);/*Headers with empty values * is ignored for output*/ } } Else if(Qlen >0|| Con->request.http_method! =http_method_head) { /** Qlen = 0 is important for redirects (301, ...) as they may * has a content. Browsers is waiting for a Content otherwise*/buffer_copy_off_t (SRV-tmp_buf, Qlen); Response_header_overwrite (SRV, con, Const_str_len ("Content-length"), Const_buf_len (srv->tmp_buf)); } } } Else { /** * The file isn ' t finished yet, but we had all headers * ~ To get keep-alive we either need: *-Content-length: ... (http/1.0 and http/1.0) or *-transfer-encoding:chunked (http/1.1)*/ if(((Con->parsed_response & http_content_length) = =0) &&(Con->response.transfer_encoding & http_transfer_encoding_chunked) = =0) ) {con->keep_alive =0; } /** If the backend sent a connection:close, follow the wish * * Note:if the backend sent Connec Tion:keep-alive, but no content-length, we * 'll close the connection. That ' s fine. We can always decide the close * the connection * * Fixme:to is nice we should remove the connection : ... */ if(Con->parsed_response &http_connection) { /** A subrequest disable keep-alive although the client wanted it*/ if(Con->keep_alive &&!con->response.keep_alive) {Con->keep_alive =0; } } } if(Con->request.http_method = =http_method_head) { /** * A HEAD request has the same as a GET * without the content*/Con->file_finished =1; Chunkqueue_reset (Con-write_queue); Con->response.transfer_encoding &= ~http_transfer_encoding_chunked; } http_response_write_header (SRV, con); return 0;}
First, the function determines whether the mode of the connection is direct, and if so, that the connection is not processed by the plug-in and is handled by the server itself. This determines the connection's request method, and if it is option, sets the Allow value. Also empty the write_queue because there is no data to return. Next, in the following switch statement, comparing the value of Http_status, if 204,205,304, the server does not need to return a file to the client, only the response headers and its previous parts. Both here and before the option method is set con->file_finished to 1. file_finished the static file used to mark the client request has been sent out (the meaning of this file_finished is vague, it is unclear whether the file is sent, or the file to be sent is set to send ... It could be a bug ... If you have any ideas, please do not hesitate to enlighten me! )。 Both do not need to send a file to the client, so set it to 1, the sender will skip the file send directly. Switch's default branch handles 4XX errors and returns the appropriate error prompt file.
After the switch statement is made, the value of the If Judgment file_finished is followed. A value of 1 indicates that no file data needs to be returned to the client. For 1xx,204 and 304 states, set Content-length to a null value. If the method is head, then the server may need to return some data to set the corresponding content-length. If the value of file_finished is 0, set the value of Keep_alive. At the end of the Connection_handle_write_prepare function, call Http_response_write_header writes headers to Write_queue, waiting to be returned to the client. If all goes well, then the state machine enters the con_state_write.
Because the data may not be finished at once, especially when sending large files. Therefore, in the Con_state_write state first Judge Write_queue time is empty, that is, there is no data has not been sent. Also determine if the connection is writable. If there is data and can be written, call Connection_handle_write to send the data. If no data is writable or the connection is not writable, then the switch (con->state) statement is exited. At the same time, because the state machine state is not changed, the IF statement behind the switch causes the server to exit the large while loop and enter the small switch (con->state) statement behind the loop. This switch statement has been described earlier. Here, enter the Con_state_write branch, if there is data writable and the connection is writable and does not reach the traffic limit, then register the connection in Fdevent, otherwise, delete the connection.
Let's go into the Connection_handle_write function to see if the data is writable and the connection is writable. The Connection_handle_write function first calls the Network_write_chunkqueue function in the switch statement. As the name implies, this function is to write the data in the Write_queue back to the client. function Network_write_chunkqueue first to determine whether the current connection traffic exceeds the limit, if so, do not send any data, directly add the connection to the job list (joblist), so that the other connection to send data. If there is no overrun, set the Tcp_cork option first. This option can send multiple write calls together to improve the efficiency of the delivery. Content readers about tcp_cork are free to Google. The next step is to call the Srv->network_backend_wirte () function to write the actual data. There are many true definitions of this function, in the network_*.c file. The server sets different values in the Network_init function of NETWORK.C based on the current operating environment. In order to improve the efficiency of the server, different OS will provide some different methods to improve the speed of sending files. The traditional read in write method requires 4 copies of the data (from the disk to the kernel buffer, from the kernel buffer to the user buffer, from the user buffer to the kernel buffer of the network interface, and finally from the network interface's kernel buffer to the network device buffer. ), the OS will provide some specific methods to reduce the number of copies, the specific reader can Google "direct io", there are many articles about this. In order to make full use of these methods to improve the performance of the server, LIGHTTPD will use these specific interfaces on different OS, so it is necessary to implement multiple Network_backend_wirte functions. These implementations are basically the same, so this is the only implementation in NETWORK_WRITE.C. The body of the function is a large loop that iterates through all the chunk. If the type of CHUNK is Mem_chunk, then the data in this CHUNK is in memory, and the data is sent directly by calling write or the Send function under Windows. If it is a file_chunk type, indicating that this chunk represents a file, then if the run environment has mmap function, the mmap mapping file is used and sent, otherwise read in write. If this chunk is sent, continue sending the next chunk. If you have not finished sending (chunk_finished=0), exit the loop,The function is then exited. The server then returns to Network_write_chunkqueue, doing some statistical work, and once again checks whether the traffic on the connection is overrun. The last server is returned to Connection_handle_write. If Network_write_chunkqueue returns-1, the server is faulted. The state machine enters the con_state_error. If 2 is returned, the client closes the connection and the state machine enters Con_state_error. Returning 0 indicates that the send is complete and enters the next state. Returns 1 indicates that the data was not sent out, and the tag is_wirtable is 0.
After returning from the Connection_handler_write function, if the data is not sent, the state machine is still
Con_state_write State, then the connection is added to the fdevent system, waiting for the next data to be sent. Repeat the above procedure to know that the send is complete or error.
If the data is sent, the state machine enters the con_state_response_end state. In this state, the server first calls Plugins_call_handle_request_done to notify all plug-in connections to finish. Then determine if the connection is maintained, and if so, set the state machine to Con_state_request_start. If not, then call Plugins_call_handle_connection_close first to notify all plug-in connections to close. Then close the connection. Finally, reset con to clear the previous requested data.
At this point, the request processing is complete. In general, the process of returning to response is straightforward and there is not much to understand. It is not easy to understand the details of the HTTP protocol in some cases of processing, the reader can refer to the RFC understanding.
The following article will analyze some error handling procedures.
This article by http://www.wifidog.pro/2015/04/10/wifidog%E8%AE%A4%E8%AF%81lighttpd-1.html compilation, reprint please indicate the source
WiFiDog authentication comes with HTTP server Lighttpd1.4.20 source Analysis state Machine return response