httpserve.c (7200B)
1 /************************************************************************* 2 * This file is part of Crosroads 1.23, a load balancer and fail over 3 * utility for TCP. Copyright (c) Karel Kubat, distributed under GPL. 4 * Visit http://crossroads.e-tunity.com for information. 5 *************************************************************************/ 6 #include "crossroads.h" 7 8 void http_serve (int clientsock) { 9 int serversock = -1, is_continuation, i; 10 HttpHeader *clientreq, *serverresp; 11 int client_persisting, server_persisting; 12 unsigned char *cp; 13 unsigned size; 14 15 /* Fork off a servicer if we're in daemon mode. */ 16 if (fork_tcp_servicer(-1)) 17 return; 18 19 /* We're the child branch now of fork_tcp_servicer(). 20 * Here's the logic of the HTTP services. 21 * - Get the client's request; 22 * - If we're not yet connected to a back end: determine the back 23 * end and connect to it 24 * - Apply all server-directed headers 25 * - Send client's request to the server 26 * - Read back the server's response 27 * - Apply all client-directed headers 28 * - Send the server's response to the client 29 * - Redo from start; this might be a Keep-Alive connection 30 */ 31 32 while (1) { 33 /* Assume both client and server want to persist. */ 34 client_persisting = server_persisting = 1; 35 36 /* Get the initial client headers */ 37 clientreq = http_header_new(); 38 http_header_read (clientreq, clientsock, dir_client_to_server); 39 40 /* Determine the back end if necessary. */ 41 if (serversock == -1) { 42 if ( (serversock = http_serversocket (clientreq, 43 &is_continuation)) < 1 ) 44 http_error (clientsock); 45 46 incr_client_count(); 47 if (is_continuation) 48 log_activity_continuation(); 49 else 50 log_activity_start (); 51 } 52 53 /* Put all server-directed headers in place. */ 54 for (i = 0; 55 i < activeservice->backend[current_backend].naddserverheader; 56 i++) 57 http_header_addheader (clientreq, 58 activeservice->backend[current_backend].addserverheader[i]); 59 for (i = 0; 60 i < activeservice->backend[current_backend].nsetserverheader; 61 i++) 62 http_header_setheader (clientreq, 63 activeservice->backend[current_backend].setserverheader[i]); 64 for (i = 0; 65 i < activeservice->backend[current_backend].nappendserverheader; 66 i++) 67 http_header_appendheader (clientreq, 68 activeservice->backend[current_backend].appendserverheader[i]); 69 70 /* See RFC2616. We're actually a proxy here. Clients that talk 71 * HTTP/1.0 MUST be treated as single shot connection clients, even 72 * when they specify Connection: Keep-Alive. */ 73 if (http_header_httpver (clientreq) < 1.1) { 74 msg ("Service %s: Client talks HTTP < 1.1, forcing closing " 75 "connections", activeservice->name); 76 http_header_setheader (clientreq, "Connection: close"); 77 http_header_setheader (clientreq, "Proxy-Connection: close"); 78 client_persisting = 0; 79 } else if (http_header_connectiontype (clientreq) == con_close) { 80 msg ("Service %s: Client asks for connection closing", 81 activeservice->name); 82 client_persisting = 0; 83 } 84 85 /* Send client's headers to the back end. */ 86 http_header_write (clientreq, serversock, dir_client_to_server); 87 88 /* Copy body from client to server */ 89 http_copy (clientreq, clientsock, serversock, dir_client_to_server); 90 if ( (cp = net_buffer (dir_client_to_server, &size)) ) 91 net_write (serversock, cp, size, 0); 92 93 /* Get server response. */ 94 serverresp = http_header_new(); 95 http_header_read (serverresp, serversock, dir_server_to_client); 96 97 /* According to RFC1616, we should convert chunked output 98 * from the server to one big message. 99 * We won't do that here, it breaks streaming. 100 * But we will close connections in the case of chunked transfer. */ 101 if (client_persisting && 102 http_header_val (serverresp, "transfer-encoding")) { 103 msg ("Service %s: Server is sending chunks, forcing singleshot", 104 activeservice->name); 105 server_persisting = 0; 106 http_header_setheader (serverresp, "Connection: close"); 107 http_header_setheader (serverresp, "Proxy-Connection: close"); 108 } 109 110 /* Put client-directed headers in place. */ 111 for (i = 0; 112 i < activeservice->backend[current_backend].naddclientheader; 113 i++) 114 http_header_addheader (serverresp, 115 activeservice->backend[current_backend].addclientheader[i]); 116 for (i = 0; 117 i < activeservice->backend[current_backend].nsetclientheader; 118 i++) 119 http_header_setheader (serverresp, 120 activeservice->backend[current_backend].setclientheader[i]); 121 for (i = 0; 122 i < activeservice->backend[current_backend].nappendclientheader; 123 i++) 124 http_header_appendheader (serverresp, 125 activeservice->backend[current_backend].appendclientheader[i]); 126 127 /* According to RFC1616, we should convert chunked output 128 * from the server to one big message. 129 * We won't do that here, it breaks streaming. 130 * But we will close connections in the case of chunked transfer. */ 131 if (client_persisting && 132 http_header_val (serverresp, "transfer-encoding")) { 133 msg ("Service %s: Server is sending chunks, forcing singleshot", 134 activeservice->name); 135 server_persisting = 0; 136 client_persisting = 0; 137 http_header_setheader (serverresp, "Connection: close"); 138 http_header_setheader (serverresp, "Proxy-Connection: close"); 139 } 140 141 /* If the server wants to close the connection, then we must 142 * inform the client. */ 143 if (client_persisting) { 144 if (http_header_httpver (serverresp) < 1.1) { 145 msg ("Service %s: Server talks HTTP < 1.1, forcing closing " 146 "connections", activeservice->name); 147 http_header_setheader (serverresp, "Connection: close"); 148 http_header_setheader (serverresp, "Proxy-Connection: close"); 149 server_persisting = 0; 150 } else if (http_header_connectiontype (serverresp) == con_close) { 151 msg ("Service %s: Server asks for connection closing", 152 activeservice->name); 153 http_header_setheader (serverresp, "Connection: close"); 154 http_header_setheader (serverresp, "Proxy-Connection: close"); 155 server_persisting = 0; 156 } 157 } 158 159 /* Send server headers to the client. */ 160 http_header_write (serverresp, clientsock, dir_server_to_client); 161 162 /* Copy body from server to cliet and flush network buffers if any */ 163 http_copy (serverresp, serversock, clientsock, dir_server_to_client); 164 if ( (cp = net_buffer (dir_server_to_client, &size)) ) 165 net_write (clientsock, cp, size, 0); 166 167 /* Free up info from this request/response chat. */ 168 http_header_free (clientreq); 169 http_header_free (serverresp); 170 171 /* Should we loop around? Go to copy-thru mode if either the 172 * client or the server wants to stop. */ 173 if (!client_persisting || !server_persisting) { 174 msg ("Service %s: non-persistent connection", 175 activeservice->name); 176 break; 177 } 178 179 msg ("Service %s: Analyzing following requests over same TCP link", 180 activeservice->name); 181 } 182 183 /* Copy through all that arrives on back end or client. 184 * This doesn't return... */ 185 msg ("Service %s: HTTP service done analyzing, entering copy-thru mode", 186 activeservice->name); 187 188 /* This doesn't return */ 189 copysockets (clientsock, serversock); 190 191 }