diff options
author | erdgeist <> | 2009-01-05 18:05:39 +0000 |
---|---|---|
committer | erdgeist <> | 2009-01-05 18:05:39 +0000 |
commit | 779d6c235ff8fe5284fd10dc82a9b99e7fa38d06 (patch) | |
tree | 043369d2a98a45b902e5d0968e28d78c1771b143 /ot_http.c | |
parent | 8bdc0d73f6f0bcaf83b7fb3d39e79e8fa4e6050d (diff) |
* http and udp routines now use thread local buffers passed in workstruct containers. In other words they do not use static_buffer anymore and are considered to be thread safe.
* the new workstruct also introduces a well defined buffer and result passing path
* a new function scan_find_keywords is a wrapper around scan_urlencoded_query that maps keys in url to values passed in an array of ot_keywords structs
* this new function cleans up much of url parameter parsing work, where read_ptr and write_ptr have been introduced rather than the confusing char *c, *data variables
* I now use memcmp instead of byte_diff to allow compiler to optimize constant size string compares
* got rid of UTORRENT_1600_WORKAROUND
* livesync_ticker is now only called from one (currently main) thread to avoid race conditions
Diffstat (limited to 'ot_http.c')
-rw-r--r-- | ot_http.c | 454 |
1 files changed, 186 insertions, 268 deletions
@@ -27,26 +27,19 @@ | |||
27 | #include "ot_accesslist.h" | 27 | #include "ot_accesslist.h" |
28 | 28 | ||
29 | #define OT_MAXMULTISCRAPE_COUNT 64 | 29 | #define OT_MAXMULTISCRAPE_COUNT 64 |
30 | static ot_hash multiscrape_buf[OT_MAXMULTISCRAPE_COUNT]; | ||
31 | extern char *g_redirecturl; | 30 | extern char *g_redirecturl; |
32 | 31 | ||
33 | enum { | 32 | enum { |
34 | SUCCESS_HTTP_HEADER_LENGTH = 80, | 33 | SUCCESS_HTTP_HEADER_LENGTH = 80, |
35 | SUCCESS_HTTP_HEADER_LENGHT_CONTENT_ENCODING = 32, | 34 | SUCCESS_HTTP_HEADER_LENGTH_CONTENT_ENCODING = 32, |
36 | SUCCESS_HTTP_SIZE_OFF = 17 }; | 35 | SUCCESS_HTTP_SIZE_OFF = 17 }; |
37 | 36 | ||
38 | /* Our static output buffer */ | ||
39 | static char static_outbuf[8192]; | ||
40 | #ifdef _DEBUG_HTTPERROR | ||
41 | static char debug_request[8192]; | ||
42 | #endif | ||
43 | |||
44 | #ifdef _DEBUG_PEERID | 37 | #ifdef _DEBUG_PEERID |
45 | size_t g_this_peerid_len = 0; | 38 | size_t g_this_peerid_len = 0; |
46 | char *g_this_peerid_data = NULL; | 39 | char *g_this_peerid_data = NULL; |
47 | #endif | 40 | #endif |
48 | 41 | ||
49 | static void http_senddata( const int64 client_socket, char *buffer, size_t size ) { | 42 | static void http_senddata( const int64 client_socket, struct ot_workstruct *ws ) { |
50 | struct http_data *h = io_getcookie( client_socket ); | 43 | struct http_data *h = io_getcookie( client_socket ); |
51 | ssize_t written_size; | 44 | ssize_t written_size; |
52 | 45 | ||
@@ -56,22 +49,22 @@ static void http_senddata( const int64 client_socket, char *buffer, size_t size | |||
56 | array_reset( &h->data.request ); | 49 | array_reset( &h->data.request ); |
57 | } | 50 | } |
58 | 51 | ||
59 | written_size = write( client_socket, buffer, size ); | 52 | written_size = write( client_socket, ws->reply, ws->reply_size ); |
60 | if( ( written_size < 0 ) || ( (size_t)written_size == size ) ) { | 53 | if( ( written_size < 0 ) || ( written_size == ws->reply_size ) ) { |
61 | free( h ); io_close( client_socket ); | 54 | free( h ); io_close( client_socket ); |
62 | } else { | 55 | } else { |
63 | char * outbuf; | 56 | char * outbuf; |
64 | tai6464 t; | 57 | tai6464 t; |
65 | 58 | ||
66 | if( !h ) return; | 59 | if( !h ) return; |
67 | if( !( outbuf = malloc( size - written_size ) ) ) { | 60 | if( !( outbuf = malloc( ws->reply_size - written_size ) ) ) { |
68 | free(h); io_close( client_socket ); | 61 | free(h); io_close( client_socket ); |
69 | return; | 62 | return; |
70 | } | 63 | } |
71 | 64 | ||
72 | iob_reset( &h->data.batch ); | 65 | iob_reset( &h->data.batch ); |
73 | memmove( outbuf, buffer + written_size, size - written_size ); | 66 | memmove( outbuf, ws->reply + written_size, ws->reply_size - written_size ); |
74 | iob_addbuf_free( &h->data.batch, outbuf, size - written_size ); | 67 | iob_addbuf_free( &h->data.batch, outbuf, ws->reply_size - written_size ); |
75 | h->flag |= STRUCT_HTTP_FLAG_IOB_USED; | 68 | h->flag |= STRUCT_HTTP_FLAG_IOB_USED; |
76 | 69 | ||
77 | /* writeable short data sockets just have a tcp timeout */ | 70 | /* writeable short data sockets just have a tcp timeout */ |
@@ -81,33 +74,34 @@ static void http_senddata( const int64 client_socket, char *buffer, size_t size | |||
81 | } | 74 | } |
82 | } | 75 | } |
83 | 76 | ||
84 | #define HTTPERROR_302 return http_issue_error( client_socket, CODE_HTTPERROR_302 ) | 77 | #define HTTPERROR_302 return http_issue_error( client_socket, ws, CODE_HTTPERROR_302 ) |
85 | #define HTTPERROR_400 return http_issue_error( client_socket, CODE_HTTPERROR_400 ) | 78 | #define HTTPERROR_400 return http_issue_error( client_socket, ws, CODE_HTTPERROR_400 ) |
86 | #define HTTPERROR_400_PARAM return http_issue_error( client_socket, CODE_HTTPERROR_400_PARAM ) | 79 | #define HTTPERROR_400_PARAM return http_issue_error( client_socket, ws, CODE_HTTPERROR_400_PARAM ) |
87 | #define HTTPERROR_400_COMPACT return http_issue_error( client_socket, CODE_HTTPERROR_400_COMPACT ) | 80 | #define HTTPERROR_400_COMPACT return http_issue_error( client_socket, ws, CODE_HTTPERROR_400_COMPACT ) |
88 | #define HTTPERROR_403_IP return http_issue_error( client_socket, CODE_HTTPERROR_403_IP ) | 81 | #define HTTPERROR_400_DOUBLEHASH return http_issue_error( client_socket, ws, CODE_HTTPERROR_400_PARAM ) |
89 | #define HTTPERROR_404 return http_issue_error( client_socket, CODE_HTTPERROR_404 ) | 82 | #define HTTPERROR_403_IP return http_issue_error( client_socket, ws, CODE_HTTPERROR_403_IP ) |
90 | #define HTTPERROR_500 return http_issue_error( client_socket, CODE_HTTPERROR_500 ) | 83 | #define HTTPERROR_404 return http_issue_error( client_socket, ws, CODE_HTTPERROR_404 ) |
91 | ssize_t http_issue_error( const int64 client_socket, int code ) { | 84 | #define HTTPERROR_500 return http_issue_error( client_socket, ws, CODE_HTTPERROR_500 ) |
85 | ssize_t http_issue_error( const int64 client_socket, struct ot_workstruct *ws, int code ) { | ||
92 | char *error_code[] = { "302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", | 86 | char *error_code[] = { "302 Found", "400 Invalid Request", "400 Invalid Request", "400 Invalid Request", |
93 | "403 Access Denied", "404 Not Found", "500 Internal Server Error" }; | 87 | "403 Access Denied", "404 Not Found", "500 Internal Server Error" }; |
94 | char *title = error_code[code]; | 88 | char *title = error_code[code]; |
95 | size_t reply_size; | ||
96 | 89 | ||
90 | ws->reply = ws->outbuf; | ||
97 | if( code == CODE_HTTPERROR_302 ) | 91 | if( code == CODE_HTTPERROR_302 ) |
98 | reply_size = sprintf( static_outbuf, "HTTP/1.0 302 Found\r\nContent-Length: 0\r\nLocation: %s\r\n\r\n", g_redirecturl ); | 92 | ws->reply_size = snprintf( ws->reply, ws->outbuf_size, "HTTP/1.0 302 Found\r\nContent-Length: 0\r\nLocation: %s\r\n\r\n", g_redirecturl ); |
99 | else | 93 | else |
100 | reply_size = sprintf( static_outbuf, "HTTP/1.0 %s\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: %zd\r\n\r\n<title>%s</title>\n", title, strlen(title)+16-4,title+4); | 94 | ws->reply_size = snprintf( ws->reply, ws->outbuf_size, "HTTP/1.0 %s\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: %zd\r\n\r\n<title>%s</title>\n", title, strlen(title)+16-4,title+4); |
101 | 95 | ||
102 | #ifdef _DEBUG_HTTPERROR | 96 | #ifdef _DEBUG_HTTPERROR |
103 | fprintf( stderr, "DEBUG: invalid request was: %s\n", debug_request ); | 97 | fprintf( stderr, "DEBUG: invalid request was: %s\n", ws->debugbuf ); |
104 | #endif | 98 | #endif |
105 | stats_issue_event( EVENT_FAILED, FLAG_TCP, code ); | 99 | stats_issue_event( EVENT_FAILED, FLAG_TCP, code ); |
106 | http_senddata( client_socket, static_outbuf, reply_size); | 100 | http_senddata( client_socket, ws ); |
107 | return -2; | 101 | return ws->reply_size = -2; |
108 | } | 102 | } |
109 | 103 | ||
110 | ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct iovec *iovector ) { | 104 | ssize_t http_sendiovecdata( const int64 client_socket, struct ot_workstruct *ws, int iovec_entries, struct iovec *iovector ) { |
111 | struct http_data *h = io_getcookie( client_socket ); | 105 | struct http_data *h = io_getcookie( client_socket ); |
112 | char *header; | 106 | char *header; |
113 | int i; | 107 | int i; |
@@ -136,7 +130,7 @@ ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct | |||
136 | } | 130 | } |
137 | 131 | ||
138 | /* Prepare space for http header */ | 132 | /* Prepare space for http header */ |
139 | header = malloc( SUCCESS_HTTP_HEADER_LENGTH + SUCCESS_HTTP_HEADER_LENGHT_CONTENT_ENCODING ); | 133 | header = malloc( SUCCESS_HTTP_HEADER_LENGTH + SUCCESS_HTTP_HEADER_LENGTH_CONTENT_ENCODING ); |
140 | if( !header ) { | 134 | if( !header ) { |
141 | iovec_free( &iovec_entries, &iovector ); | 135 | iovec_free( &iovec_entries, &iovector ); |
142 | HTTPERROR_500; | 136 | HTTPERROR_500; |
@@ -159,7 +153,7 @@ ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct | |||
159 | 153 | ||
160 | h->flag |= STRUCT_HTTP_FLAG_IOB_USED; | 154 | h->flag |= STRUCT_HTTP_FLAG_IOB_USED; |
161 | 155 | ||
162 | /* writeable sockets timeout after 10 minutes) */ | 156 | /* writeable sockets timeout after 10 minutes */ |
163 | taia_now( &t ); taia_addsec( &t, &t, OT_CLIENT_TIMEOUT_SEND ); | 157 | taia_now( &t ); taia_addsec( &t, &t, OT_CLIENT_TIMEOUT_SEND ); |
164 | io_timeout( client_socket, t ); | 158 | io_timeout( client_socket, t ); |
165 | io_dontwantread( client_socket ); | 159 | io_dontwantread( client_socket ); |
@@ -167,9 +161,21 @@ ssize_t http_sendiovecdata( const int64 client_socket, int iovec_entries, struct | |||
167 | return 0; | 161 | return 0; |
168 | } | 162 | } |
169 | 163 | ||
170 | static ssize_t http_handle_stats( const int64 client_socket, char *data, char *d, size_t l ) { | 164 | static ssize_t http_handle_stats( const int64 client_socket, struct ot_workstruct *ws, char *read_ptr ) { |
171 | char *c = data; | 165 | static const ot_keywords keywords_main[] = |
166 | { { "mode", 1 }, {"format", 2 }, { NULL, -3 } }; | ||
167 | static const ot_keywords keywords_mode[] = | ||
168 | { { "peer", TASK_STATS_PEERS }, { "conn", TASK_STATS_CONNS }, { "scrp", TASK_STATS_SCRAPE }, { "udp4", TASK_STATS_UDP }, | ||
169 | { "busy", TASK_STATS_BUSY_NETWORKS }, { "torr", TASK_STATS_TORRENTS }, { "fscr", TASK_STATS_FULLSCRAPE }, | ||
170 | { "s24s", TASK_STATS_SLASH24S }, { "tpbs", TASK_STATS_TPB }, { "herr", TASK_STATS_HTTPERRORS }, | ||
171 | { "top10", TASK_STATS_TOP10 }, { "renew", TASK_STATS_RENEW }, { "syncs", TASK_STATS_SYNCS }, { "version", TASK_STATS_VERSION }, | ||
172 | { "startstop", TASK_STATS_STARTSTOP }, { "toraddrem", TASK_STATS_TORADDREM }, { NULL, -3 } }; | ||
173 | static const ot_keywords keywords_format[] = | ||
174 | { { "bin", TASK_FULLSCRAPE_TPB_BINARY }, { "ben", TASK_FULLSCRAPE }, { "url", TASK_FULLSCRAPE_TPB_URLENCODED }, | ||
175 | { "txt", TASK_FULLSCRAPE_TPB_ASCII }, { NULL, -3 } }; | ||
176 | |||
172 | int mode = TASK_STATS_PEERS, scanon = 1, format = 0; | 177 | int mode = TASK_STATS_PEERS, scanon = 1, format = 0; |
178 | |||
173 | #ifdef WANT_RESTRICT_STATS | 179 | #ifdef WANT_RESTRICT_STATS |
174 | struct http_data *h = io_getcookie( client_socket ); | 180 | struct http_data *h = io_getcookie( client_socket ); |
175 | 181 | ||
@@ -178,97 +184,26 @@ static ssize_t http_handle_stats( const int64 client_socket, char *data, char *d | |||
178 | #endif | 184 | #endif |
179 | 185 | ||
180 | while( scanon ) { | 186 | while( scanon ) { |
181 | switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) { | 187 | switch( scan_find_keywords( keywords_main, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) { |
182 | case -2: scanon = 0; break; /* TERMINATOR */ | 188 | case -2: scanon = 0; break; /* TERMINATOR */ |
183 | case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ | 189 | case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ |
184 | default: scan_urlencoded_skipvalue( &c ); break; | 190 | case -3: scan_urlencoded_skipvalue( &read_ptr ); break; |
185 | case 4: | 191 | case 1: /* matched "mode" */ |
186 | if( byte_diff(data,4,"mode")) { | 192 | if( ( mode = scan_find_keywords( keywords_mode, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; |
187 | scan_urlencoded_skipvalue( &c ); | ||
188 | continue; | ||
189 | } | ||
190 | switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) { | ||
191 | case 4: | ||
192 | if( !byte_diff(data,4,"peer")) | ||
193 | mode = TASK_STATS_PEERS; | ||
194 | else if( !byte_diff(data,4,"conn")) | ||
195 | mode = TASK_STATS_CONNS; | ||
196 | else if( !byte_diff(data,4,"scrp")) | ||
197 | mode = TASK_STATS_SCRAPE; | ||
198 | else if( !byte_diff(data,4,"tcp4")) | ||
199 | mode = TASK_STATS_TCP; | ||
200 | else if( !byte_diff(data,4,"udp4")) | ||
201 | mode = TASK_STATS_UDP; | ||
202 | else if( !byte_diff(data,4,"busy")) | ||
203 | mode = TASK_STATS_BUSY_NETWORKS; | ||
204 | else if( !byte_diff(data,4,"torr")) | ||
205 | mode = TASK_STATS_TORRENTS; | ||
206 | else if( !byte_diff(data,4,"fscr")) | ||
207 | mode = TASK_STATS_FULLSCRAPE; | ||
208 | else if( !byte_diff(data,4,"s24s")) | ||
209 | mode = TASK_STATS_SLASH24S; | ||
210 | else if( !byte_diff(data,4,"tpbs")) | ||
211 | mode = TASK_STATS_TPB; | ||
212 | else if( !byte_diff(data,4,"herr")) | ||
213 | mode = TASK_STATS_HTTPERRORS; | ||
214 | else | ||
215 | HTTPERROR_400_PARAM; | ||
216 | break; | ||
217 | case 5: | ||
218 | if( !byte_diff(data,5,"top10")) | ||
219 | mode = TASK_STATS_TOP10; | ||
220 | else if( !byte_diff(data,5,"renew")) | ||
221 | mode = TASK_STATS_RENEW; | ||
222 | else if( !byte_diff(data,5,"syncs")) | ||
223 | mode = TASK_STATS_SYNCS; | ||
224 | else | ||
225 | HTTPERROR_400_PARAM; | ||
226 | break; | ||
227 | case 7: | ||
228 | if( !byte_diff(data,7,"version")) | ||
229 | mode = TASK_STATS_VERSION; | ||
230 | else | ||
231 | HTTPERROR_400_PARAM; | ||
232 | break; | ||
233 | case 9: | ||
234 | if( !byte_diff(data,9,"startstop")) | ||
235 | mode = TASK_STATS_STARTSTOP; | ||
236 | else if( !byte_diff(data,9,"toraddrem")) | ||
237 | mode = TASK_STATS_TORADDREM; | ||
238 | else | ||
239 | HTTPERROR_400_PARAM; | ||
240 | break; | ||
241 | } | ||
242 | break; | 193 | break; |
243 | case 6: | 194 | case 2: /* matched "format" */ |
244 | if( byte_diff(data,6,"format")) { | 195 | if( ( format = scan_find_keywords( keywords_format, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; |
245 | scan_urlencoded_skipvalue( &c ); | ||
246 | continue; | ||
247 | } | ||
248 | if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 3 ) HTTPERROR_400_PARAM; | ||
249 | if( !byte_diff(data,3,"bin")) | ||
250 | format = TASK_FULLSCRAPE_TPB_BINARY; | ||
251 | else if( !byte_diff(data,3,"ben")) | ||
252 | format = TASK_FULLSCRAPE; | ||
253 | else if( !byte_diff(data,3,"url")) | ||
254 | format = TASK_FULLSCRAPE_TPB_URLENCODED; | ||
255 | else if( !byte_diff(data,3,"txt")) | ||
256 | format = TASK_FULLSCRAPE_TPB_ASCII; | ||
257 | else | ||
258 | HTTPERROR_400_PARAM; | ||
259 | break; | 196 | break; |
260 | } | 197 | } |
261 | } | 198 | } |
262 | 199 | ||
263 | /* Touch variable */ | ||
264 | d=d; | ||
265 | #ifdef WANT_FULLSCRAPE | 200 | #ifdef WANT_FULLSCRAPE |
266 | if( mode == TASK_STATS_TPB ) { | 201 | if( mode == TASK_STATS_TPB ) { |
267 | struct http_data* h = io_getcookie( client_socket ); | 202 | struct http_data* h = io_getcookie( client_socket ); |
268 | tai6464 t; | 203 | tai6464 t; |
269 | #ifdef WANT_COMPRESSION_GZIP | 204 | #ifdef WANT_COMPRESSION_GZIP |
270 | d[l-1] = 0; | 205 | ws->request[ws->request_size] = 0; |
271 | if( strstr( d, "gzip" ) ) { | 206 | if( strstr( read_ptr - 1, "gzip" ) ) { |
272 | h->flag |= STRUCT_HTTP_FLAG_GZIP; | 207 | h->flag |= STRUCT_HTTP_FLAG_GZIP; |
273 | format |= TASK_FLAG_GZIP; | 208 | format |= TASK_FLAG_GZIP; |
274 | } | 209 | } |
@@ -280,7 +215,7 @@ static ssize_t http_handle_stats( const int64 client_socket, char *data, char *d | |||
280 | taia_uint( &t, 0 ); io_timeout( client_socket, t ); | 215 | taia_uint( &t, 0 ); io_timeout( client_socket, t ); |
281 | fullscrape_deliver( client_socket, format ); | 216 | fullscrape_deliver( client_socket, format ); |
282 | io_dontwantread( client_socket ); | 217 | io_dontwantread( client_socket ); |
283 | return -2; | 218 | return ws->reply_size = -2; |
284 | } | 219 | } |
285 | #endif | 220 | #endif |
286 | 221 | ||
@@ -290,27 +225,24 @@ static ssize_t http_handle_stats( const int64 client_socket, char *data, char *d | |||
290 | /* Complex stats also include expensive memory debugging tools */ | 225 | /* Complex stats also include expensive memory debugging tools */ |
291 | taia_uint( &t, 0 ); io_timeout( client_socket, t ); | 226 | taia_uint( &t, 0 ); io_timeout( client_socket, t ); |
292 | stats_deliver( client_socket, mode ); | 227 | stats_deliver( client_socket, mode ); |
293 | return -2; | 228 | return ws->reply_size = -2; |
294 | } | 229 | } |
295 | 230 | ||
296 | /* Simple stats can be answerred immediately */ | 231 | /* Simple stats can be answerred immediately */ |
297 | if( !( l = return_stats_for_tracker( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH, mode, 0 ) ) ) HTTPERROR_500; | 232 | if( !( ws->reply_size = return_stats_for_tracker( ws->reply, mode, 0 ) ) ) HTTPERROR_500; |
298 | 233 | ||
299 | return l; | 234 | return ws->reply_size; |
300 | } | 235 | } |
301 | 236 | ||
302 | #ifdef WANT_FULLSCRAPE | 237 | #ifdef WANT_FULLSCRAPE |
303 | static ssize_t http_handle_fullscrape( const int64 client_socket, char *d, size_t l ) { | 238 | static ssize_t http_handle_fullscrape( const int64 client_socket, struct ot_workstruct *ws ) { |
304 | struct http_data* h = io_getcookie( client_socket ); | 239 | struct http_data* h = io_getcookie( client_socket ); |
305 | int format = 0; | 240 | int format = 0; |
306 | tai6464 t; | 241 | tai6464 t; |
307 | 242 | ||
308 | /* Touch variables */ | ||
309 | d=d;l=l; | ||
310 | |||
311 | #ifdef WANT_COMPRESSION_GZIP | 243 | #ifdef WANT_COMPRESSION_GZIP |
312 | d[l-1] = 0; | 244 | ws->request[ws->request_size-1] = 0; |
313 | if( strstr( d, "gzip" ) ) { | 245 | if( strstr( ws->request, "gzip" ) ) { |
314 | h->flag |= STRUCT_HTTP_FLAG_GZIP; | 246 | h->flag |= STRUCT_HTTP_FLAG_GZIP; |
315 | format = TASK_FLAG_GZIP; | 247 | format = TASK_FLAG_GZIP; |
316 | stats_issue_event( EVENT_FULLSCRAPE_REQUEST_GZIP, *(int*)h->ip, 0 ); | 248 | stats_issue_event( EVENT_FULLSCRAPE_REQUEST_GZIP, *(int*)h->ip, 0 ); |
@@ -319,7 +251,7 @@ static ssize_t http_handle_fullscrape( const int64 client_socket, char *d, size_ | |||
319 | stats_issue_event( EVENT_FULLSCRAPE_REQUEST, *(int*)h->ip, 0 ); | 251 | stats_issue_event( EVENT_FULLSCRAPE_REQUEST, *(int*)h->ip, 0 ); |
320 | 252 | ||
321 | #ifdef _DEBUG_HTTPERROR | 253 | #ifdef _DEBUG_HTTPERROR |
322 | write( 2, debug_request, l ); | 254 | write( 2, ws->debugbuf, ws->debugbuf_size ); |
323 | #endif | 255 | #endif |
324 | 256 | ||
325 | /* Pass this task to the worker thread */ | 257 | /* Pass this task to the worker thread */ |
@@ -328,72 +260,70 @@ write( 2, debug_request, l ); | |||
328 | taia_uint( &t, 0 ); io_timeout( client_socket, t ); | 260 | taia_uint( &t, 0 ); io_timeout( client_socket, t ); |
329 | fullscrape_deliver( client_socket, TASK_FULLSCRAPE | format ); | 261 | fullscrape_deliver( client_socket, TASK_FULLSCRAPE | format ); |
330 | io_dontwantread( client_socket ); | 262 | io_dontwantread( client_socket ); |
331 | return -2; | 263 | return ws->reply_size = -2; |
332 | } | 264 | } |
333 | #endif | 265 | #endif |
266 | static ssize_t http_handle_scrape( const int64 client_socket, struct ot_workstruct *ws, char *read_ptr ) { | ||
267 | static const ot_keywords keywords_scrape[] = { { "info_hash", 1 }, { NULL, -3 } }; | ||
334 | 268 | ||
335 | static ssize_t http_handle_scrape( const int64 client_socket, char *data ) { | 269 | ot_hash * multiscrape_buf = (ot_hash*)ws->request; |
336 | int scanon = 1, numwant = 0; | 270 | int scanon = 1, numwant = 0; |
337 | char *c = data; | ||
338 | size_t l; | ||
339 | 271 | ||
340 | /* This is to hack around stupid clients that send "scrape ?info_hash" */ | 272 | /* This is to hack around stupid clients that send "scrape ?info_hash" */ |
341 | if( c[-1] != '?' ) { | 273 | if( read_ptr[-1] != '?' ) { |
342 | while( ( *c != '?' ) && ( *c != '\n' ) ) ++c; | 274 | while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr; |
343 | if( *c == '\n' ) HTTPERROR_400_PARAM; | 275 | if( *read_ptr == '\n' ) HTTPERROR_400_PARAM; |
344 | ++c; | 276 | ++read_ptr; |
345 | } | 277 | } |
346 | 278 | ||
347 | while( scanon ) { | 279 | while( scanon ) { |
348 | switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) { | 280 | switch( scan_find_keywords( keywords_scrape, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) { |
349 | case -2: scanon = 0; break; /* TERMINATOR */ | 281 | case -2: scanon = 0; break; /* TERMINATOR */ |
350 | case -1: | 282 | default: HTTPERROR_400_PARAM; /* PARSE ERROR */ |
351 | if( numwant ) | 283 | case -3: scan_urlencoded_skipvalue( &read_ptr ); break; |
352 | goto UTORRENT1600_WORKAROUND; | 284 | case 1: /* matched "info_hash" */ |
353 | HTTPERROR_400_PARAM; /* PARSE ERROR */ | ||
354 | default: scan_urlencoded_skipvalue( &c ); break; | ||
355 | case 9: | ||
356 | if(byte_diff(data,9,"info_hash")) { | ||
357 | scan_urlencoded_skipvalue( &c ); | ||
358 | continue; | ||
359 | } | ||
360 | /* ignore this, when we have less than 20 bytes */ | 285 | /* ignore this, when we have less than 20 bytes */ |
361 | if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != (ssize_t)sizeof(ot_hash) ) { | 286 | if( scan_urlencoded_query( &read_ptr, (char*)(multiscrape_buf + numwant++), SCAN_SEARCHPATH_VALUE ) != (ssize_t)sizeof(ot_hash) ) |
362 | #ifdef WANT_UTORRENT1600_WORKAROUND | ||
363 | if( data[20] != '?' ) | ||
364 | #endif | ||
365 | HTTPERROR_400_PARAM; | 287 | HTTPERROR_400_PARAM; |
366 | } | ||
367 | if( numwant < OT_MAXMULTISCRAPE_COUNT ) | ||
368 | memmove( multiscrape_buf + numwant++, data, sizeof(ot_hash) ); | ||
369 | break; | 288 | break; |
370 | } | 289 | } |
371 | } | 290 | } |
372 | 291 | ||
373 | UTORRENT1600_WORKAROUND: | ||
374 | |||
375 | /* No info_hash found? Inform user */ | 292 | /* No info_hash found? Inform user */ |
376 | if( !numwant ) HTTPERROR_400_PARAM; | 293 | if( !numwant ) HTTPERROR_400_PARAM; |
294 | |||
295 | /* Limit number of hashes to process */ | ||
296 | if( numwant > OT_MAXMULTISCRAPE_COUNT ) | ||
297 | numwant = OT_MAXMULTISCRAPE_COUNT; | ||
377 | 298 | ||
378 | /* Enough for http header + whole scrape string */ | 299 | /* Enough for http header + whole scrape string */ |
379 | if( !( l = return_tcp_scrape_for_torrent( multiscrape_buf, numwant, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf ) ) ) HTTPERROR_500; | 300 | if( !( ws->reply_size = return_tcp_scrape_for_torrent( multiscrape_buf, numwant, ws->reply ) ) ) HTTPERROR_500; |
380 | stats_issue_event( EVENT_SCRAPE, FLAG_TCP, l ); | 301 | stats_issue_event( EVENT_SCRAPE, FLAG_TCP, ws->reply_size ); |
381 | return l; | 302 | return ws->reply_size; |
382 | } | 303 | } |
383 | 304 | ||
384 | static ssize_t http_handle_announce( const int64 client_socket, char *data ) { | 305 | static ot_keywords keywords_announce[] = { { "port", 1 }, { "left", 2 }, { "event", 3 }, { "numwant", 4 }, { "compact", 5 }, { "info_hash", 6 }, |
385 | char *c = data; | 306 | #ifdef WANT_IP_FROM_QUERY_STRING |
386 | int numwant, tmp, scanon; | 307 | { "ip", 7 }, |
387 | ot_peer peer; | 308 | #endif |
388 | ot_hash *hash = NULL; | 309 | #ifdef _DEBUG_PEERID |
310 | { "peer_id", 8 }, | ||
311 | #endif | ||
312 | { NULL, -3 } }; | ||
313 | static ot_keywords keywords_announce_event[] = { { "completed", 1 }, { "stopped", 2 }, { NULL, -3 } }; | ||
314 | static ssize_t http_handle_announce( const int64 client_socket, struct ot_workstruct *ws, char *read_ptr ) { | ||
315 | int numwant, tmp, scanon; | ||
316 | ot_peer peer; | ||
317 | ot_hash *hash = NULL; | ||
389 | unsigned short port = htons(6881); | 318 | unsigned short port = htons(6881); |
390 | ssize_t len; | 319 | char *write_ptr; |
391 | 320 | ssize_t len; | |
321 | |||
392 | /* This is to hack around stupid clients that send "announce ?info_hash" */ | 322 | /* This is to hack around stupid clients that send "announce ?info_hash" */ |
393 | if( c[-1] != '?' ) { | 323 | if( read_ptr[-1] != '?' ) { |
394 | while( ( *c != '?' ) && ( *c != '\n' ) ) ++c; | 324 | while( ( *read_ptr != '?' ) && ( *read_ptr != '\n' ) ) ++read_ptr; |
395 | if( *c == '\n' ) HTTPERROR_400_PARAM; | 325 | if( *read_ptr == '\n' ) HTTPERROR_400_PARAM; |
396 | ++c; | 326 | ++read_ptr; |
397 | } | 327 | } |
398 | 328 | ||
399 | OT_SETIP( &peer, ((struct http_data*)io_getcookie( client_socket ) )->ip ); | 329 | OT_SETIP( &peer, ((struct http_data*)io_getcookie( client_socket ) )->ip ); |
@@ -403,168 +333,156 @@ static ssize_t http_handle_announce( const int64 client_socket, char *data ) { | |||
403 | scanon = 1; | 333 | scanon = 1; |
404 | 334 | ||
405 | #ifdef _DEBUG_PEERID | 335 | #ifdef _DEBUG_PEERID |
406 | g_this_peerid_data = NULL; | 336 | ws->peer_id = NULL; |
407 | #endif | 337 | #endif |
408 | 338 | ||
409 | while( scanon ) { | 339 | while( scanon ) { |
410 | switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_PARAM ) ) { | 340 | switch( scan_find_keywords(keywords_announce, &read_ptr, SCAN_SEARCHPATH_PARAM ) ) { |
411 | case -2: scanon = 0; break; /* TERMINATOR */ | 341 | case -2: scanon = 0; break; /* TERMINATOR */ |
412 | case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ | 342 | case -1: HTTPERROR_400_PARAM; /* PARSE ERROR */ |
413 | default: scan_urlencoded_skipvalue( &c ); break; | 343 | case -3: scan_urlencoded_skipvalue( &read_ptr ); break; |
414 | #ifdef WANT_IP_FROM_QUERY_STRING | 344 | case 1: /* matched "port" */ |
415 | case 2: | 345 | len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); |
416 | if(!byte_diff(data,2,"ip")) { | 346 | if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) || ( tmp > 0xffff ) ) HTTPERROR_400_PARAM; |
417 | unsigned char ip[4]; | 347 | port = htons( tmp ); OT_SETPORT( &peer, &port ); |
418 | len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); | ||
419 | if( ( len <= 0 ) || scan_fixed_ip( data, len, ip ) ) HTTPERROR_400_PARAM; | ||
420 | OT_SETIP( &peer, ip ); | ||
421 | } else | ||
422 | scan_urlencoded_skipvalue( &c ); | ||
423 | break; | ||
424 | #endif | ||
425 | case 4: | ||
426 | if( !byte_diff( data, 4, "port" ) ) { | ||
427 | len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); | ||
428 | if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) || ( tmp > 0xffff ) ) HTTPERROR_400_PARAM; | ||
429 | port = htons( tmp ); OT_SETPORT( &peer, &port ); | ||
430 | } else if( !byte_diff( data, 4, "left" ) ) { | ||
431 | if( ( len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; | ||
432 | if( scan_fixed_int( data, len, &tmp ) ) tmp = 0; | ||
433 | if( !tmp ) OT_PEERFLAG( &peer ) |= PEER_FLAG_SEEDING; | ||
434 | } else | ||
435 | scan_urlencoded_skipvalue( &c ); | ||
436 | break; | 348 | break; |
437 | case 5: | 349 | case 2: /* matched "left" */ |
438 | if( byte_diff( data, 5, "event" ) ) | 350 | if( ( len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) ) <= 0 ) HTTPERROR_400_PARAM; |
439 | scan_urlencoded_skipvalue( &c ); | 351 | if( scan_fixed_int( write_ptr, len, &tmp ) ) tmp = 0; |
440 | else switch( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) ) { | 352 | if( !tmp ) OT_PEERFLAG( &peer ) |= PEER_FLAG_SEEDING; |
441 | case -1: | 353 | break; |
442 | HTTPERROR_400_PARAM; | 354 | case 3: /* matched "event" */ |
443 | case 7: | 355 | switch( scan_find_keywords( keywords_announce_event, &read_ptr, SCAN_SEARCHPATH_VALUE ) ) { |
444 | if( !byte_diff( data, 7, "stopped" ) ) OT_PEERFLAG( &peer ) |= PEER_FLAG_STOPPED; | 356 | case -1: HTTPERROR_400_PARAM; |
445 | break; | 357 | case 1: /* matched "completed" */ |
446 | case 9: | 358 | OT_PEERFLAG( &peer ) |= PEER_FLAG_COMPLETED; |
447 | if( !byte_diff( data, 9, "completed" ) ) OT_PEERFLAG( &peer ) |= PEER_FLAG_COMPLETED; | 359 | break; |
448 | default: /* Fall through intended */ | 360 | case 2: /* matched "stopped" */ |
449 | break; | 361 | OT_PEERFLAG( &peer ) |= PEER_FLAG_STOPPED; |
362 | break; | ||
363 | default: | ||
364 | break; | ||
450 | } | 365 | } |
451 | break; | 366 | break; |
452 | case 7: | 367 | case 4: /* matched "numwant" */ |
453 | if(!byte_diff(data,7,"numwant")) { | 368 | len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); |
454 | len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); | 369 | if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &numwant ) ) HTTPERROR_400_PARAM; |
455 | if( ( len <= 0 ) || scan_fixed_int( data, len, &numwant ) ) HTTPERROR_400_PARAM; | 370 | if( numwant < 0 ) numwant = 50; |
456 | if( numwant < 0 ) numwant = 50; | 371 | if( numwant > 200 ) numwant = 200; |
457 | if( numwant > 200 ) numwant = 200; | ||
458 | } else if(!byte_diff(data,7,"compact")) { | ||
459 | len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); | ||
460 | if( ( len <= 0 ) || scan_fixed_int( data, len, &tmp ) ) HTTPERROR_400_PARAM; | ||
461 | if( !tmp ) HTTPERROR_400_COMPACT; | ||
462 | } else | ||
463 | #ifdef _DEBUG_PEERID | ||
464 | if(!byte_diff(data,7,"peer_id")) { | ||
465 | g_this_peerid_len = scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ); | ||
466 | g_this_peerid_data = g_this_peerid_len > 0 ? data : 0; | ||
467 | } else | ||
468 | #endif | ||
469 | scan_urlencoded_skipvalue( &c ); | ||
470 | break; | 372 | break; |
471 | case 9: | 373 | case 5: /* matched "compact" */ |
472 | if(byte_diff(data,9,"info_hash")) { | 374 | len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); |
473 | scan_urlencoded_skipvalue( &c ); | 375 | if( ( len <= 0 ) || scan_fixed_int( write_ptr, len, &tmp ) ) HTTPERROR_400_PARAM; |
474 | continue; | 376 | if( !tmp ) HTTPERROR_400_COMPACT; |
475 | } | 377 | break; |
378 | case 6: /* matched "info_hash" */ | ||
379 | if( hash ) HTTPERROR_400_DOUBLEHASH; | ||
476 | /* ignore this, when we have less than 20 bytes */ | 380 | /* ignore this, when we have less than 20 bytes */ |
477 | if( scan_urlencoded_query( &c, data = c, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM; | 381 | if( scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ) != 20 ) HTTPERROR_400_PARAM; |
478 | hash = (ot_hash*)data; | 382 | hash = (ot_hash*)write_ptr; |
383 | break; | ||
384 | #ifdef WANT_IP_FROM_QUERY_STRING | ||
385 | case 7: /* matched "ip" */ | ||
386 | len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); | ||
387 | if( ( len <= 0 ) || scan_fixed_ip( write_ptr, len, (unsigned char*)/*tmp*/ws->reply ) ) HTTPERROR_400_PARAM; | ||
388 | OT_SETIP( &peer, /*tmp*/ws->reply ); | ||
479 | break; | 389 | break; |
390 | #endif | ||
391 | #ifdef _DEBUG_PEERID | ||
392 | case 8: /* matched "peer_id" */ | ||
393 | ws->peer_id_size = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE ); | ||
394 | ws->peer_id = ws->peer_id_size > 0 ? write_ptr : 0; | ||
395 | break; | ||
396 | #endif | ||
480 | } | 397 | } |
481 | } | 398 | } |
482 | 399 | ||
483 | /* Scanned whole query string */ | 400 | /* Scanned whole query string */ |
484 | if( !hash ) | 401 | if( !hash ) |
485 | return sprintf( static_outbuf + SUCCESS_HTTP_HEADER_LENGTH, "d14:failure reason80:Your client forgot to send your torrent's info_hash. Please upgrade your client.e" ); | 402 | return ws->reply_size = sprintf( ws->reply, "d14:failure reason80:Your client forgot to send your torrent's info_hash. Please upgrade your client.e" ); |
486 | 403 | ||
487 | if( OT_PEERFLAG( &peer ) & PEER_FLAG_STOPPED ) | 404 | if( OT_PEERFLAG( &peer ) & PEER_FLAG_STOPPED ) |
488 | len = remove_peer_from_torrent( hash, &peer, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf, FLAG_TCP ); | 405 | ws->reply_size = remove_peer_from_torrent( hash, &peer, ws->reply, FLAG_TCP ); |
489 | else | 406 | else |
490 | len = add_peer_to_torrent_and_return_peers(hash, &peer, FLAG_TCP, numwant, SUCCESS_HTTP_HEADER_LENGTH + static_outbuf); | 407 | ws->reply_size = add_peer_to_torrent_and_return_peers(hash, &peer, FLAG_TCP, numwant, ws->reply ); |
491 | 408 | ||
492 | if( !len ) HTTPERROR_500; | 409 | if( !ws->reply_size ) HTTPERROR_500; |
493 | 410 | ||
494 | stats_issue_event( EVENT_ANNOUNCE, FLAG_TCP, len); | 411 | stats_issue_event( EVENT_ANNOUNCE, FLAG_TCP, ws->reply_size); |
495 | return len; | 412 | return ws->reply_size; |
496 | } | 413 | } |
497 | 414 | ||
498 | ssize_t http_handle_request( const int64 client_socket, char *data, size_t recv_length ) { | 415 | ssize_t http_handle_request( const int64 client_socket, struct ot_workstruct *ws ) { |
499 | char *c, *recv_header=data; | 416 | ssize_t reply_off, len; |
500 | ssize_t reply_size = 0, reply_off, len; | 417 | char *read_ptr = ws->request, *write_ptr; |
501 | 418 | ||
502 | #ifdef _DEBUG_HTTPERROR | 419 | #ifdef _DEBUG_HTTPERROR |
503 | if( recv_length >= sizeof( debug_request ) ) | 420 | reply_off = ws->request_size; |
504 | recv_length = sizeof( debug_request) - 1; | 421 | if( ws->request_size >= (ssize_t)ws->debugbuf_size ) |
505 | memmove( debug_request, recv_header, recv_length ); | 422 | reply_off = ws->debugbuf_size - 1; |
506 | debug_request[ recv_length ] = 0; | 423 | memmove( ws->debugbuf, ws->request, reply_off ); |
424 | ws->debugbuf[ reply_off ] = 0; | ||
507 | #endif | 425 | #endif |
508 | 426 | ||
427 | /* Tell subroutines where to put reply data */ | ||
428 | ws->reply = ws->outbuf + SUCCESS_HTTP_HEADER_LENGTH; | ||
429 | |||
509 | /* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */ | 430 | /* This one implicitely tests strlen < 5, too -- remember, it is \n terminated */ |
510 | if( byte_diff( data, 5, "GET /") ) HTTPERROR_400; | 431 | if( memcmp( read_ptr, "GET /", 5) ) HTTPERROR_400; |
511 | 432 | ||
512 | /* Skip leading '/' */ | 433 | /* Skip leading '/' */ |
513 | for( c = data+4; *c == '/'; ++c); | 434 | for( read_ptr+=4; *read_ptr == '/'; ++read_ptr); |
514 | 435 | ||
515 | /* Try to parse the request. | 436 | /* Try to parse the request. |
516 | In reality we abandoned requiring the url to be correct. This now | 437 | In reality we abandoned requiring the url to be correct. This now |
517 | only decodes url encoded characters, we check for announces and | 438 | only decodes url encoded characters, we check for announces and |
518 | scrapes by looking for "a*" or "sc" */ | 439 | scrapes by looking for "a*" or "sc" */ |
519 | len = scan_urlencoded_query( &c, data = c, SCAN_PATH ); | 440 | len = scan_urlencoded_query( &read_ptr, write_ptr = read_ptr, SCAN_PATH ); |
520 | 441 | ||
521 | /* If parsing returned an error, leave with not found */ | 442 | /* If parsing returned an error, leave with not found */ |
522 | if( g_redirecturl && ( len == -2 ) ) HTTPERROR_302; | 443 | if( g_redirecturl && ( len == -2 ) ) HTTPERROR_302; |
523 | if( len <= 0 ) HTTPERROR_404; | 444 | if( len <= 0 ) HTTPERROR_404; |
524 | 445 | ||
525 | /* This is the hardcore match for announce*/ | 446 | /* This is the hardcore match for announce*/ |
526 | if( ( *data == 'a' ) || ( *data == '?' ) ) | 447 | if( ( *write_ptr == 'a' ) || ( *write_ptr == '?' ) ) |
527 | reply_size = http_handle_announce( client_socket, c ); | 448 | http_handle_announce( client_socket, ws, read_ptr ); |
528 | #ifdef WANT_FULLSCRAPE | 449 | #ifdef WANT_FULLSCRAPE |
529 | else if( !byte_diff( data, 12, "scrape HTTP/" ) ) | 450 | else if( !memcmp( write_ptr, "scrape HTTP/", 12 ) ) |
530 | reply_size = http_handle_fullscrape( client_socket, recv_header, recv_length ); | 451 | http_handle_fullscrape( client_socket, ws ); |
531 | #endif | 452 | #endif |
532 | /* This is the hardcore match for scrape */ | 453 | /* This is the hardcore match for scrape */ |
533 | else if( !byte_diff( data, 2, "sc" ) ) | 454 | else if( !memcmp( write_ptr, "sc", 2 ) ) |
534 | reply_size = http_handle_scrape( client_socket, c ); | 455 | http_handle_scrape( client_socket, ws, read_ptr ); |
535 | /* All the rest is matched the standard way */ | 456 | /* All the rest is matched the standard way */ |
536 | else switch( len ) { | 457 | else if( !memcmp( write_ptr, "stats", 5) ) |
537 | case 5: /* stats ? */ | 458 | http_handle_stats( client_socket, ws, read_ptr ); |
538 | if( byte_diff( data, 5, "stats") ) HTTPERROR_404; | 459 | else |
539 | reply_size = http_handle_stats( client_socket, c, recv_header, recv_length ); | ||
540 | break; | ||
541 | default: | ||
542 | HTTPERROR_404; | 460 | HTTPERROR_404; |
543 | } | ||
544 | 461 | ||
545 | /* If routines handled sending themselves, just return */ | 462 | /* If routines handled sending themselves, just return */ |
546 | if( reply_size == -2 ) return 0; | 463 | if( ws->reply_size == -2 ) return 0; |
547 | /* If routine failed, let http error take over */ | 464 | /* If routine failed, let http error take over */ |
548 | if( reply_size == -1 ) HTTPERROR_500; | 465 | if( ws->reply_size == -1 ) HTTPERROR_500; |
549 | 466 | ||
550 | /* This one is rather ugly, so I take you step by step through it. | 467 | /* This one is rather ugly, so I take you step by step through it. |
551 | 468 | ||
552 | 1. In order to avoid having two buffers, one for header and one for content, we allow all above functions from trackerlogic to | 469 | 1. In order to avoid having two buffers, one for header and one for content, we allow all above functions from trackerlogic to |
553 | write to a fixed location, leaving SUCCESS_HTTP_HEADER_LENGTH bytes in our static buffer, which is enough for the static string | 470 | write to a fixed location, leaving SUCCESS_HTTP_HEADER_LENGTH bytes in our work buffer, which is enough for the static string |
554 | plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for its expansion and calculate | 471 | plus dynamic space needed to expand our Content-Length value. We reserve SUCCESS_HTTP_SIZE_OFF for its expansion and calculate |
555 | the space NOT needed to expand in reply_off | 472 | the space NOT needed to expand in reply_off |
556 | */ | 473 | */ |
557 | reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf( static_outbuf, 0, "%zd", reply_size ); | 474 | reply_off = SUCCESS_HTTP_SIZE_OFF - snprintf( ws->outbuf, 0, "%zd", ws->reply_size ); |
558 | 475 | ws->reply = ws->outbuf + reply_off; | |
476 | |||
559 | /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete | 477 | /* 2. Now we sprintf our header so that sprintf writes its terminating '\0' exactly one byte before content starts. Complete |
560 | packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */ | 478 | packet size is increased by size of header plus one byte '\n', we will copy over '\0' in next step */ |
561 | reply_size += 1 + sprintf( static_outbuf + reply_off, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", reply_size ); | 479 | ws->reply_size += 1 + sprintf( ws->reply, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zd\r\n\r", ws->reply_size ); |
562 | 480 | ||
563 | /* 3. Finally we join both blocks neatly */ | 481 | /* 3. Finally we join both blocks neatly */ |
564 | static_outbuf[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n'; | 482 | ws->outbuf[ SUCCESS_HTTP_HEADER_LENGTH - 1 ] = '\n'; |
565 | 483 | ||
566 | http_senddata( client_socket, static_outbuf + reply_off, reply_size ); | 484 | http_senddata( client_socket, ws ); |
567 | return reply_size; | 485 | return ws->reply_size; |
568 | } | 486 | } |
569 | 487 | ||
570 | const char *g_version_http_c = "$Source$: $Revision$\n"; | 488 | const char *g_version_http_c = "$Source$: $Revision$\n"; |