winhttp: Support WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL.
[wine.git] / dlls / winhttp / request.c
blob543532c31d84a6c1057e33becd5c653193b5a052
1 /*
2 * Copyright 2004 Mike McCormack for CodeWeavers
3 * Copyright 2006 Rob Shearman for CodeWeavers
4 * Copyright 2008, 2011 Hans Leidekker for CodeWeavers
5 * Copyright 2009 Juan Lang
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <assert.h>
23 #include <stdarg.h>
24 #include <wchar.h>
26 #define COBJMACROS
27 #include "windef.h"
28 #include "winbase.h"
29 #include "ws2tcpip.h"
30 #include "ole2.h"
31 #include "initguid.h"
32 #include "httprequest.h"
33 #include "httprequestid.h"
34 #include "schannel.h"
35 #include "winhttp.h"
36 #include "ntsecapi.h"
37 #include "winternl.h"
39 #include "wine/debug.h"
40 #include "winhttp_private.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(winhttp);
44 #define DEFAULT_KEEP_ALIVE_TIMEOUT 30000
46 static const WCHAR *attribute_table[] =
48 L"Mime-Version", /* WINHTTP_QUERY_MIME_VERSION = 0 */
49 L"Content-Type" , /* WINHTTP_QUERY_CONTENT_TYPE = 1 */
50 L"Content-Transfer-Encoding", /* WINHTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
51 L"Content-ID", /* WINHTTP_QUERY_CONTENT_ID = 3 */
52 NULL, /* WINHTTP_QUERY_CONTENT_DESCRIPTION = 4 */
53 L"Content-Length", /* WINHTTP_QUERY_CONTENT_LENGTH = 5 */
54 L"Content-Language", /* WINHTTP_QUERY_CONTENT_LANGUAGE = 6 */
55 L"Allow", /* WINHTTP_QUERY_ALLOW = 7 */
56 L"Public", /* WINHTTP_QUERY_PUBLIC = 8 */
57 L"Date", /* WINHTTP_QUERY_DATE = 9 */
58 L"Expires", /* WINHTTP_QUERY_EXPIRES = 10 */
59 L"Last-Modified", /* WINHTTP_QUERY_LAST_MODIFIEDcw = 11 */
60 NULL, /* WINHTTP_QUERY_MESSAGE_ID = 12 */
61 L"URI", /* WINHTTP_QUERY_URI = 13 */
62 L"From", /* WINHTTP_QUERY_DERIVED_FROM = 14 */
63 NULL, /* WINHTTP_QUERY_COST = 15 */
64 NULL, /* WINHTTP_QUERY_LINK = 16 */
65 L"Pragma", /* WINHTTP_QUERY_PRAGMA = 17 */
66 NULL, /* WINHTTP_QUERY_VERSION = 18 */
67 L"Status", /* WINHTTP_QUERY_STATUS_CODE = 19 */
68 NULL, /* WINHTTP_QUERY_STATUS_TEXT = 20 */
69 NULL, /* WINHTTP_QUERY_RAW_HEADERS = 21 */
70 NULL, /* WINHTTP_QUERY_RAW_HEADERS_CRLF = 22 */
71 L"Connection", /* WINHTTP_QUERY_CONNECTION = 23 */
72 L"Accept", /* WINHTTP_QUERY_ACCEPT = 24 */
73 L"Accept-Charset", /* WINHTTP_QUERY_ACCEPT_CHARSET = 25 */
74 L"Accept-Encoding", /* WINHTTP_QUERY_ACCEPT_ENCODING = 26 */
75 L"Accept-Language", /* WINHTTP_QUERY_ACCEPT_LANGUAGE = 27 */
76 L"Authorization", /* WINHTTP_QUERY_AUTHORIZATION = 28 */
77 L"Content-Encoding", /* WINHTTP_QUERY_CONTENT_ENCODING = 29 */
78 NULL, /* WINHTTP_QUERY_FORWARDED = 30 */
79 NULL, /* WINHTTP_QUERY_FROM = 31 */
80 L"If-Modified-Since", /* WINHTTP_QUERY_IF_MODIFIED_SINCE = 32 */
81 L"Location", /* WINHTTP_QUERY_LOCATION = 33 */
82 NULL, /* WINHTTP_QUERY_ORIG_URI = 34 */
83 L"Referer", /* WINHTTP_QUERY_REFERER = 35 */
84 L"Retry-After", /* WINHTTP_QUERY_RETRY_AFTER = 36 */
85 L"Server", /* WINHTTP_QUERY_SERVER = 37 */
86 NULL, /* WINHTTP_TITLE = 38 */
87 L"User-Agent", /* WINHTTP_QUERY_USER_AGENT = 39 */
88 L"WWW-Authenticate", /* WINHTTP_QUERY_WWW_AUTHENTICATE = 40 */
89 L"Proxy-Authenticate", /* WINHTTP_QUERY_PROXY_AUTHENTICATE = 41 */
90 L"Accept-Ranges", /* WINHTTP_QUERY_ACCEPT_RANGES = 42 */
91 L"Set-Cookie", /* WINHTTP_QUERY_SET_COOKIE = 43 */
92 L"Cookie", /* WINHTTP_QUERY_COOKIE = 44 */
93 NULL, /* WINHTTP_QUERY_REQUEST_METHOD = 45 */
94 NULL, /* WINHTTP_QUERY_REFRESH = 46 */
95 NULL, /* WINHTTP_QUERY_CONTENT_DISPOSITION = 47 */
96 L"Age", /* WINHTTP_QUERY_AGE = 48 */
97 L"Cache-Control", /* WINHTTP_QUERY_CACHE_CONTROL = 49 */
98 L"Content-Base", /* WINHTTP_QUERY_CONTENT_BASE = 50 */
99 L"Content-Location", /* WINHTTP_QUERY_CONTENT_LOCATION = 51 */
100 L"Content-MD5", /* WINHTTP_QUERY_CONTENT_MD5 = 52 */
101 L"Content-Range", /* WINHTTP_QUERY_CONTENT_RANGE = 53 */
102 L"ETag", /* WINHTTP_QUERY_ETAG = 54 */
103 L"Host", /* WINHTTP_QUERY_HOST = 55 */
104 L"If-Match", /* WINHTTP_QUERY_IF_MATCH = 56 */
105 L"If-None-Match", /* WINHTTP_QUERY_IF_NONE_MATCH = 57 */
106 L"If-Range", /* WINHTTP_QUERY_IF_RANGE = 58 */
107 L"If-Unmodified-Since", /* WINHTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
108 L"Max-Forwards", /* WINHTTP_QUERY_MAX_FORWARDS = 60 */
109 L"Proxy-Authorization", /* WINHTTP_QUERY_PROXY_AUTHORIZATION = 61 */
110 L"Range", /* WINHTTP_QUERY_RANGE = 62 */
111 L"Transfer-Encoding", /* WINHTTP_QUERY_TRANSFER_ENCODING = 63 */
112 L"Upgrade", /* WINHTTP_QUERY_UPGRADE = 64 */
113 L"Vary", /* WINHTTP_QUERY_VARY = 65 */
114 L"Via", /* WINHTTP_QUERY_VIA = 66 */
115 L"Warning", /* WINHTTP_QUERY_WARNING = 67 */
116 L"Expect", /* WINHTTP_QUERY_EXPECT = 68 */
117 L"Proxy-Connection", /* WINHTTP_QUERY_PROXY_CONNECTION = 69 */
118 L"Unless-Modified-Since", /* WINHTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
119 NULL, /* WINHTTP_QUERY_PROXY_SUPPORT = 75 */
120 NULL, /* WINHTTP_QUERY_AUTHENTICATION_INFO = 76 */
121 NULL, /* WINHTTP_QUERY_PASSPORT_URLS = 77 */
122 NULL /* WINHTTP_QUERY_PASSPORT_CONFIG = 78 */
125 void init_queue( struct queue *queue )
127 InitializeSRWLock( &queue->lock );
128 list_init( &queue->queued_tasks );
129 queue->callback_running = FALSE;
132 void stop_queue( struct queue *queue )
134 assert( list_empty( &queue->queued_tasks ));
135 TRACE("stopped %p\n", queue);
138 static void addref_task( struct task_header *task )
140 InterlockedIncrement( &task->refs );
143 static void release_task( struct task_header *task )
145 if (!InterlockedDecrement( &task->refs ))
146 free( task );
149 static struct task_header *get_next_task( struct queue *queue, struct task_header *prev_task )
151 struct task_header *task;
152 struct list *entry;
154 AcquireSRWLockExclusive( &queue->lock );
155 assert( queue->callback_running );
156 if (prev_task)
158 list_remove( &prev_task->entry );
159 release_task( prev_task );
161 if ((entry = list_head( &queue->queued_tasks )))
163 task = LIST_ENTRY( entry, struct task_header, entry );
164 addref_task( task );
166 else
168 task = NULL;
169 queue->callback_running = FALSE;
171 ReleaseSRWLockExclusive( &queue->lock );
172 return task;
175 static void CALLBACK task_callback( TP_CALLBACK_INSTANCE *instance, void *ctx )
177 struct task_header *task, *next_task;
178 struct queue *queue = ctx;
180 TRACE( "instance %p.\n", instance );
182 task = get_next_task( queue, NULL );
183 while (task)
185 task->callback( task, FALSE );
186 /* Queue object may be freed by release_object() unless there is another task referencing it. */
187 next_task = get_next_task( queue, task );
188 release_object( task->obj );
189 release_task( task );
190 task = next_task;
192 TRACE( "instance %p exiting.\n", instance );
195 static DWORD queue_task( struct queue *queue, TASK_CALLBACK task, struct task_header *task_hdr,
196 struct object_header *obj )
198 BOOL callback_running;
200 TRACE("queueing %p in %p\n", task_hdr, queue);
201 task_hdr->callback = task;
202 task_hdr->completion_sent = 0;
203 task_hdr->refs = 1;
204 task_hdr->obj = obj;
205 addref_object( obj );
207 AcquireSRWLockExclusive( &queue->lock );
208 list_add_tail( &queue->queued_tasks, &task_hdr->entry );
209 if (!(callback_running = queue->callback_running))
211 if ((queue->callback_running = TrySubmitThreadpoolCallback( task_callback, queue, NULL )))
212 callback_running = TRUE;
213 else
214 list_remove( &task_hdr->entry );
216 ReleaseSRWLockExclusive( &queue->lock );
218 if (!callback_running)
220 release_object( obj );
221 free( task_hdr );
222 ERR( "Submiting threadpool callback failed, err %lu.\n", GetLastError() );
223 return ERROR_OUTOFMEMORY;
226 return ERROR_SUCCESS;
229 static BOOL task_needs_completion( struct task_header *task_hdr )
231 return !InterlockedExchange( &task_hdr->completion_sent, 1 );
234 static BOOL cancel_queue( struct queue *queue )
236 struct task_header *task_hdr, *found;
237 BOOL cancelled = FALSE;
239 while (1)
241 AcquireSRWLockExclusive( &queue->lock );
242 found = NULL;
243 LIST_FOR_EACH_ENTRY( task_hdr, &queue->queued_tasks, struct task_header, entry )
245 if (task_needs_completion( task_hdr ))
247 found = task_hdr;
248 addref_task( found );
249 break;
252 ReleaseSRWLockExclusive( &queue->lock );
253 if (!found) break;
254 cancelled = TRUE;
255 found->callback( found, TRUE );
256 release_task( found );
258 return cancelled;
261 static void free_header( struct header *header )
263 free( header->field );
264 free( header->value );
265 free( header );
268 static BOOL valid_token_char( WCHAR c )
270 if (c < 32 || c == 127) return FALSE;
271 switch (c)
273 case '(': case ')':
274 case '<': case '>':
275 case '@': case ',':
276 case ';': case ':':
277 case '\\': case '\"':
278 case '/': case '[':
279 case ']': case '?':
280 case '=': case '{':
281 case '}': case ' ':
282 case '\t':
283 return FALSE;
284 default:
285 return TRUE;
289 static struct header *parse_header( const WCHAR *string, size_t string_len )
291 const WCHAR *p, *q;
292 struct header *header;
293 int len;
295 p = string;
296 if (!(q = wcschr( p, ':' )))
298 WARN("no ':' in line %s\n", debugstr_w(string));
299 return NULL;
301 if (q == string)
303 WARN("empty field name in line %s\n", debugstr_w(string));
304 return NULL;
306 while (*p != ':')
308 if (!valid_token_char( *p ))
310 WARN("invalid character in field name %s\n", debugstr_w(string));
311 return NULL;
313 p++;
315 len = q - string;
316 if (!(header = calloc( 1, sizeof(*header) ))) return NULL;
317 if (!(header->field = malloc( (len + 1) * sizeof(WCHAR) )))
319 free( header );
320 return NULL;
322 memcpy( header->field, string, len * sizeof(WCHAR) );
323 header->field[len] = 0;
325 q++; /* skip past colon */
326 while (*q == ' ') q++;
327 len = (string + string_len) - q;
329 if (!(header->value = malloc( (len + 1) * sizeof(WCHAR) )))
331 free_header( header );
332 return NULL;
334 memcpy( header->value, q, len * sizeof(WCHAR) );
335 header->value[len] = 0;
337 return header;
340 static int get_header_index( struct request *request, const WCHAR *field, int requested_index, BOOL request_only )
342 int index;
344 TRACE("%s\n", debugstr_w(field));
346 for (index = 0; index < request->num_headers; index++)
348 if (wcsicmp( request->headers[index].field, field )) continue;
349 if (request_only && !request->headers[index].is_request) continue;
350 if (!request_only && request->headers[index].is_request) continue;
352 if (!requested_index) break;
353 requested_index--;
355 if (index >= request->num_headers) index = -1;
356 TRACE("returning %d\n", index);
357 return index;
360 static DWORD insert_header( struct request *request, struct header *header )
362 DWORD count = request->num_headers + 1;
363 struct header *hdrs;
365 if (request->headers)
367 if ((hdrs = realloc( request->headers, sizeof(*header) * count )))
368 memset( &hdrs[count - 1], 0, sizeof(*header) );
370 else hdrs = calloc( 1, sizeof(*header) );
371 if (!hdrs) return ERROR_OUTOFMEMORY;
373 request->headers = hdrs;
374 request->headers[count - 1].field = strdupW( header->field );
375 request->headers[count - 1].value = strdupW( header->value );
376 request->headers[count - 1].is_request = header->is_request;
377 request->num_headers = count;
378 return ERROR_SUCCESS;
381 static void delete_header( struct request *request, DWORD index )
383 if (!request->num_headers || index >= request->num_headers) return;
384 request->num_headers--;
386 free( request->headers[index].field );
387 free( request->headers[index].value );
389 memmove( &request->headers[index], &request->headers[index + 1],
390 (request->num_headers - index) * sizeof(struct header) );
391 memset( &request->headers[request->num_headers], 0, sizeof(struct header) );
394 DWORD process_header( struct request *request, const WCHAR *field, const WCHAR *value, DWORD flags, BOOL request_only )
396 int index;
397 struct header hdr;
399 TRACE( "%s: %s %#lx\n", debugstr_w(field), debugstr_w(value), flags );
401 if ((index = get_header_index( request, field, 0, request_only )) >= 0)
403 if (flags & WINHTTP_ADDREQ_FLAG_ADD_IF_NEW) return ERROR_WINHTTP_HEADER_ALREADY_EXISTS;
406 if (flags & WINHTTP_ADDREQ_FLAG_REPLACE)
408 if (index >= 0)
410 delete_header( request, index );
411 if (!value || !value[0]) return ERROR_SUCCESS;
413 else if (!(flags & WINHTTP_ADDREQ_FLAG_ADD)) return ERROR_WINHTTP_HEADER_NOT_FOUND;
415 hdr.field = (LPWSTR)field;
416 hdr.value = (LPWSTR)value;
417 hdr.is_request = request_only;
418 return insert_header( request, &hdr );
420 else if (value)
423 if ((flags & (WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA | WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON)) &&
424 index >= 0)
426 WCHAR *tmp;
427 int len, len_orig, len_value;
428 struct header *header = &request->headers[index];
430 len_orig = lstrlenW( header->value );
431 len_value = lstrlenW( value );
433 len = len_orig + len_value + 2;
434 if (!(tmp = realloc( header->value, (len + 1) * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
435 header->value = tmp;
436 header->value[len_orig++] = (flags & WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA) ? ',' : ';';
437 header->value[len_orig++] = ' ';
439 memcpy( &header->value[len_orig], value, len_value * sizeof(WCHAR) );
440 header->value[len] = 0;
441 return ERROR_SUCCESS;
443 else
445 hdr.field = (LPWSTR)field;
446 hdr.value = (LPWSTR)value;
447 hdr.is_request = request_only;
448 return insert_header( request, &hdr );
452 return ERROR_SUCCESS;
455 DWORD add_request_headers( struct request *request, const WCHAR *headers, DWORD len, DWORD flags )
457 DWORD ret = ERROR_WINHTTP_INVALID_HEADER;
458 struct header *header;
459 const WCHAR *p, *q;
461 if (len == ~0u) len = lstrlenW( headers );
462 if (!len) return ERROR_SUCCESS;
464 p = headers;
467 const WCHAR *end;
469 if (p >= headers + len) break;
471 for (q = p; q < headers + len && *q != '\r' && *q != '\n'; ++q)
473 end = q;
474 while (*q == '\r' || *q == '\n')
475 ++q;
477 if ((header = parse_header( p, end - p )))
479 ret = process_header( request, header->field, header->value, flags, TRUE );
480 free_header( header );
482 p = q;
483 } while (!ret);
485 return ret;
488 /***********************************************************************
489 * WinHttpAddRequestHeaders (winhttp.@)
491 BOOL WINAPI WinHttpAddRequestHeaders( HINTERNET hrequest, const WCHAR *headers, DWORD len, DWORD flags )
493 DWORD ret;
494 struct request *request;
496 TRACE( "%p, %s, %lu, %#lx\n", hrequest, debugstr_wn(headers, len), len, flags );
498 if (!headers || !len)
500 SetLastError( ERROR_INVALID_PARAMETER );
501 return FALSE;
503 if (!(request = (struct request *)grab_object( hrequest )))
505 SetLastError( ERROR_INVALID_HANDLE );
506 return FALSE;
508 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
510 release_object( &request->hdr );
511 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
512 return FALSE;
515 ret = add_request_headers( request, headers, len, flags );
517 release_object( &request->hdr );
518 SetLastError( ret );
519 return !ret;
522 static WCHAR *build_absolute_request_path( struct request *request, const WCHAR **path )
524 const WCHAR *scheme;
525 WCHAR *ret;
526 int len, offset;
528 scheme = (request->netconn ? request->netconn->secure : (request->hdr.flags & WINHTTP_FLAG_SECURE)) ? L"https" : L"http";
530 len = lstrlenW( scheme ) + lstrlenW( request->connect->hostname ) + 4; /* '://' + nul */
531 if (request->connect->hostport) len += 6; /* ':' between host and port, up to 5 for port */
533 len += lstrlenW( request->path );
534 if ((ret = malloc( len * sizeof(WCHAR) )))
536 offset = swprintf( ret, len, L"%s://%s", scheme, request->connect->hostname );
537 if (request->connect->hostport)
539 offset += swprintf( ret + offset, len - offset, L":%u", request->connect->hostport );
541 lstrcpyW( ret + offset, request->path );
542 if (path) *path = ret + offset;
545 return ret;
548 static WCHAR *build_request_string( struct request *request )
550 WCHAR *path, *ret;
551 unsigned int i, len;
553 if (!wcsicmp( request->connect->hostname, request->connect->servername )) path = request->path;
554 else if (!(path = build_absolute_request_path( request, NULL ))) return NULL;
556 len = lstrlenW( request->verb ) + 1 /* ' ' */;
557 len += lstrlenW( path ) + 1 /* ' ' */;
558 len += lstrlenW( request->version );
560 for (i = 0; i < request->num_headers; i++)
562 if (request->headers[i].is_request)
563 len += lstrlenW( request->headers[i].field ) + lstrlenW( request->headers[i].value ) + 4; /* '\r\n: ' */
565 len += 4; /* '\r\n\r\n' */
567 if ((ret = malloc( (len + 1) * sizeof(WCHAR) )))
569 lstrcpyW( ret, request->verb );
570 lstrcatW( ret, L" " );
571 lstrcatW( ret, path );
572 lstrcatW( ret, L" " );
573 lstrcatW( ret, request->version );
575 for (i = 0; i < request->num_headers; i++)
577 if (request->headers[i].is_request)
579 lstrcatW( ret, L"\r\n" );
580 lstrcatW( ret, request->headers[i].field );
581 lstrcatW( ret, L": " );
582 lstrcatW( ret, request->headers[i].value );
585 lstrcatW( ret, L"\r\n\r\n" );
588 if (path != request->path) free( path );
589 return ret;
592 #define QUERY_MODIFIER_MASK (WINHTTP_QUERY_FLAG_REQUEST_HEADERS | WINHTTP_QUERY_FLAG_SYSTEMTIME | WINHTTP_QUERY_FLAG_NUMBER)
594 static DWORD query_headers( struct request *request, DWORD level, const WCHAR *name, void *buffer, DWORD *buflen,
595 DWORD *index )
597 struct header *header = NULL;
598 BOOL request_only;
599 int requested_index, header_index = -1;
600 DWORD attr, len, ret = ERROR_WINHTTP_HEADER_NOT_FOUND;
602 request_only = level & WINHTTP_QUERY_FLAG_REQUEST_HEADERS;
603 requested_index = index ? *index : 0;
605 attr = level & ~QUERY_MODIFIER_MASK;
606 switch (attr)
608 case WINHTTP_QUERY_CUSTOM:
610 header_index = get_header_index( request, name, requested_index, request_only );
611 break;
613 case WINHTTP_QUERY_RAW_HEADERS:
615 WCHAR *headers, *p, *q;
617 if (request_only)
618 headers = build_request_string( request );
619 else
620 headers = request->raw_headers;
622 if (!(p = headers)) return ERROR_OUTOFMEMORY;
623 for (len = 0; *p; p++) if (*p != '\r') len++;
625 if (!buffer || len * sizeof(WCHAR) > *buflen) ret = ERROR_INSUFFICIENT_BUFFER;
626 else
628 for (p = headers, q = buffer; *p; p++, q++)
630 if (*p != '\r') *q = *p;
631 else
633 *q = 0;
634 p++; /* skip '\n' */
637 TRACE("returning data: %s\n", debugstr_wn(buffer, len));
638 if (len) len--;
639 ret = ERROR_SUCCESS;
641 *buflen = len * sizeof(WCHAR);
642 if (request_only) free( headers );
643 return ret;
645 case WINHTTP_QUERY_RAW_HEADERS_CRLF:
647 WCHAR *headers;
649 if (request_only)
650 headers = build_request_string( request );
651 else
652 headers = request->raw_headers;
654 if (!headers) return ERROR_OUTOFMEMORY;
655 len = lstrlenW( headers ) * sizeof(WCHAR);
656 if (!buffer || len + sizeof(WCHAR) > *buflen)
658 len += sizeof(WCHAR);
659 ret = ERROR_INSUFFICIENT_BUFFER;
661 else
663 memcpy( buffer, headers, len + sizeof(WCHAR) );
664 TRACE("returning data: %s\n", debugstr_wn(buffer, len / sizeof(WCHAR)));
665 ret = ERROR_SUCCESS;
667 *buflen = len;
668 if (request_only) free( headers );
669 return ret;
671 case WINHTTP_QUERY_VERSION:
672 len = lstrlenW( request->version ) * sizeof(WCHAR);
673 if (!buffer || len + sizeof(WCHAR) > *buflen)
675 len += sizeof(WCHAR);
676 ret = ERROR_INSUFFICIENT_BUFFER;
678 else
680 lstrcpyW( buffer, request->version );
681 TRACE("returning string: %s\n", debugstr_w(buffer));
682 ret = ERROR_SUCCESS;
684 *buflen = len;
685 return ret;
687 case WINHTTP_QUERY_STATUS_TEXT:
688 len = lstrlenW( request->status_text ) * sizeof(WCHAR);
689 if (!buffer || len + sizeof(WCHAR) > *buflen)
691 len += sizeof(WCHAR);
692 ret = ERROR_INSUFFICIENT_BUFFER;
694 else
696 lstrcpyW( buffer, request->status_text );
697 TRACE("returning string: %s\n", debugstr_w(buffer));
698 ret = ERROR_SUCCESS;
700 *buflen = len;
701 return ret;
703 case WINHTTP_QUERY_REQUEST_METHOD:
704 len = lstrlenW( request->verb ) * sizeof(WCHAR);
705 if (!buffer || len + sizeof(WCHAR) > *buflen)
707 len += sizeof(WCHAR);
708 ret = ERROR_INSUFFICIENT_BUFFER;
710 else
712 lstrcpyW( buffer, request->verb );
713 TRACE("returning string: %s\n", debugstr_w(buffer));
714 ret = ERROR_SUCCESS;
716 *buflen = len;
717 return ret;
719 default:
720 if (attr >= ARRAY_SIZE(attribute_table)) return ERROR_INVALID_PARAMETER;
721 if (!attribute_table[attr])
723 FIXME( "attribute %lu not implemented\n", attr );
724 return ERROR_WINHTTP_HEADER_NOT_FOUND;
726 TRACE("attribute %s\n", debugstr_w(attribute_table[attr]));
727 header_index = get_header_index( request, attribute_table[attr], requested_index, request_only );
728 break;
731 if (header_index >= 0)
733 header = &request->headers[header_index];
735 if (!header || (request_only && !header->is_request)) return ERROR_WINHTTP_HEADER_NOT_FOUND;
736 if (level & WINHTTP_QUERY_FLAG_NUMBER)
738 if (!buffer || sizeof(int) > *buflen) ret = ERROR_INSUFFICIENT_BUFFER;
739 else
741 int *number = buffer;
742 *number = wcstol( header->value, NULL, 10 );
743 TRACE("returning number: %d\n", *number);
744 ret = ERROR_SUCCESS;
746 *buflen = sizeof(int);
748 else if (level & WINHTTP_QUERY_FLAG_SYSTEMTIME)
750 SYSTEMTIME *st = buffer;
751 if (!buffer || sizeof(SYSTEMTIME) > *buflen) ret = ERROR_INSUFFICIENT_BUFFER;
752 else if (WinHttpTimeToSystemTime( header->value, st ))
754 TRACE("returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
755 st->wYear, st->wMonth, st->wDay, st->wDayOfWeek,
756 st->wHour, st->wMinute, st->wSecond, st->wMilliseconds);
757 ret = ERROR_SUCCESS;
759 *buflen = sizeof(SYSTEMTIME);
761 else if (header->value)
763 len = lstrlenW( header->value ) * sizeof(WCHAR);
764 if (!buffer || len + sizeof(WCHAR) > *buflen)
766 len += sizeof(WCHAR);
767 ret = ERROR_INSUFFICIENT_BUFFER;
769 else
771 lstrcpyW( buffer, header->value );
772 TRACE("returning string: %s\n", debugstr_w(buffer));
773 ret = ERROR_SUCCESS;
775 *buflen = len;
777 if (!ret && index) *index += 1;
778 return ret;
781 /***********************************************************************
782 * WinHttpQueryHeaders (winhttp.@)
784 BOOL WINAPI WinHttpQueryHeaders( HINTERNET hrequest, DWORD level, const WCHAR *name, void *buffer, DWORD *buflen,
785 DWORD *index )
787 DWORD ret;
788 struct request *request;
790 TRACE( "%p, %#lx, %s, %p, %p, %p\n", hrequest, level, debugstr_w(name), buffer, buflen, index );
792 if (!(request = (struct request *)grab_object( hrequest )))
794 SetLastError( ERROR_INVALID_HANDLE );
795 return FALSE;
797 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
799 release_object( &request->hdr );
800 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
801 return FALSE;
804 ret = query_headers( request, level, name, buffer, buflen, index );
806 release_object( &request->hdr );
807 SetLastError( ret );
808 return !ret;
811 static const struct
813 const WCHAR *str;
814 unsigned int len;
815 DWORD scheme;
817 auth_schemes[] =
819 { L"Basic", ARRAY_SIZE(L"Basic") - 1, WINHTTP_AUTH_SCHEME_BASIC },
820 { L"NTLM", ARRAY_SIZE(L"NTLM") - 1, WINHTTP_AUTH_SCHEME_NTLM },
821 { L"Passport", ARRAY_SIZE(L"Passport") - 1, WINHTTP_AUTH_SCHEME_PASSPORT },
822 { L"Digest", ARRAY_SIZE(L"Digest") - 1, WINHTTP_AUTH_SCHEME_DIGEST },
823 { L"Negotiate", ARRAY_SIZE(L"Negotiate") - 1, WINHTTP_AUTH_SCHEME_NEGOTIATE }
826 static enum auth_scheme scheme_from_flag( DWORD flag )
828 int i;
830 for (i = 0; i < ARRAY_SIZE( auth_schemes ); i++) if (flag == auth_schemes[i].scheme) return i;
831 return SCHEME_INVALID;
834 static DWORD auth_scheme_from_header( const WCHAR *header )
836 unsigned int i;
838 for (i = 0; i < ARRAY_SIZE( auth_schemes ); i++)
840 if (!wcsnicmp( header, auth_schemes[i].str, auth_schemes[i].len ) &&
841 (header[auth_schemes[i].len] == ' ' || !header[auth_schemes[i].len])) return auth_schemes[i].scheme;
843 return 0;
846 static DWORD query_auth_schemes( struct request *request, DWORD level, DWORD *supported, DWORD *first )
848 DWORD ret, index = 0, supported_schemes = 0, first_scheme = 0;
850 for (;;)
852 WCHAR *buffer;
853 DWORD size, scheme;
855 size = 0;
856 ret = query_headers( request, level, NULL, NULL, &size, &index );
857 if (ret != ERROR_INSUFFICIENT_BUFFER)
859 if (index) ret = ERROR_SUCCESS;
860 break;
863 if (!(buffer = malloc( size ))) return ERROR_OUTOFMEMORY;
864 if ((ret = query_headers( request, level, NULL, buffer, &size, &index )))
866 free( buffer );
867 return ret;
869 scheme = auth_scheme_from_header( buffer );
870 free( buffer );
871 if (!scheme) continue;
873 if (!first_scheme) first_scheme = scheme;
874 supported_schemes |= scheme;
877 if (!ret)
879 *supported = supported_schemes;
880 *first = first_scheme;
882 return ret;
885 /***********************************************************************
886 * WinHttpQueryAuthSchemes (winhttp.@)
888 BOOL WINAPI WinHttpQueryAuthSchemes( HINTERNET hrequest, LPDWORD supported, LPDWORD first, LPDWORD target )
890 DWORD ret;
891 struct request *request;
893 TRACE("%p, %p, %p, %p\n", hrequest, supported, first, target);
895 if (!(request = (struct request *)grab_object( hrequest )))
897 SetLastError( ERROR_INVALID_HANDLE );
898 return FALSE;
900 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
902 release_object( &request->hdr );
903 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
904 return FALSE;
906 if (!supported || !first || !target)
908 release_object( &request->hdr );
909 SetLastError( ERROR_INVALID_PARAMETER );
910 return FALSE;
914 if (!(ret = query_auth_schemes( request, WINHTTP_QUERY_WWW_AUTHENTICATE, supported, first )))
916 *target = WINHTTP_AUTH_TARGET_SERVER;
918 else if (!(ret = query_auth_schemes( request, WINHTTP_QUERY_PROXY_AUTHENTICATE, supported, first )))
920 *target = WINHTTP_AUTH_TARGET_PROXY;
922 else ret = ERROR_INVALID_OPERATION;
924 release_object( &request->hdr );
925 SetLastError( ret );
926 return !ret;
929 static UINT encode_base64( const char *bin, unsigned int len, WCHAR *base64 )
931 UINT n = 0, x;
932 static const char base64enc[] =
933 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
935 while (len > 0)
937 /* first 6 bits, all from bin[0] */
938 base64[n++] = base64enc[(bin[0] & 0xfc) >> 2];
939 x = (bin[0] & 3) << 4;
941 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
942 if (len == 1)
944 base64[n++] = base64enc[x];
945 base64[n++] = '=';
946 base64[n++] = '=';
947 break;
949 base64[n++] = base64enc[x | ((bin[1] & 0xf0) >> 4)];
950 x = (bin[1] & 0x0f) << 2;
952 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
953 if (len == 2)
955 base64[n++] = base64enc[x];
956 base64[n++] = '=';
957 break;
959 base64[n++] = base64enc[x | ((bin[2] & 0xc0) >> 6)];
961 /* last 6 bits, all from bin [2] */
962 base64[n++] = base64enc[bin[2] & 0x3f];
963 bin += 3;
964 len -= 3;
966 base64[n] = 0;
967 return n;
970 static inline char decode_char( WCHAR c )
972 if (c >= 'A' && c <= 'Z') return c - 'A';
973 if (c >= 'a' && c <= 'z') return c - 'a' + 26;
974 if (c >= '0' && c <= '9') return c - '0' + 52;
975 if (c == '+') return 62;
976 if (c == '/') return 63;
977 return 64;
980 static unsigned int decode_base64( const WCHAR *base64, unsigned int len, char *buf )
982 unsigned int i = 0;
983 char c0, c1, c2, c3;
984 const WCHAR *p = base64;
986 while (len > 4)
988 if ((c0 = decode_char( p[0] )) > 63) return 0;
989 if ((c1 = decode_char( p[1] )) > 63) return 0;
990 if ((c2 = decode_char( p[2] )) > 63) return 0;
991 if ((c3 = decode_char( p[3] )) > 63) return 0;
993 if (buf)
995 buf[i + 0] = (c0 << 2) | (c1 >> 4);
996 buf[i + 1] = (c1 << 4) | (c2 >> 2);
997 buf[i + 2] = (c2 << 6) | c3;
999 len -= 4;
1000 i += 3;
1001 p += 4;
1003 if (p[2] == '=')
1005 if ((c0 = decode_char( p[0] )) > 63) return 0;
1006 if ((c1 = decode_char( p[1] )) > 63) return 0;
1008 if (buf) buf[i] = (c0 << 2) | (c1 >> 4);
1009 i++;
1011 else if (p[3] == '=')
1013 if ((c0 = decode_char( p[0] )) > 63) return 0;
1014 if ((c1 = decode_char( p[1] )) > 63) return 0;
1015 if ((c2 = decode_char( p[2] )) > 63) return 0;
1017 if (buf)
1019 buf[i + 0] = (c0 << 2) | (c1 >> 4);
1020 buf[i + 1] = (c1 << 4) | (c2 >> 2);
1022 i += 2;
1024 else
1026 if ((c0 = decode_char( p[0] )) > 63) return 0;
1027 if ((c1 = decode_char( p[1] )) > 63) return 0;
1028 if ((c2 = decode_char( p[2] )) > 63) return 0;
1029 if ((c3 = decode_char( p[3] )) > 63) return 0;
1031 if (buf)
1033 buf[i + 0] = (c0 << 2) | (c1 >> 4);
1034 buf[i + 1] = (c1 << 4) | (c2 >> 2);
1035 buf[i + 2] = (c2 << 6) | c3;
1037 i += 3;
1039 return i;
1042 static struct authinfo *alloc_authinfo(void)
1044 struct authinfo *ret;
1046 if (!(ret = malloc( sizeof(*ret) ))) return NULL;
1048 SecInvalidateHandle( &ret->cred );
1049 SecInvalidateHandle( &ret->ctx );
1050 memset( &ret->exp, 0, sizeof(ret->exp) );
1051 ret->scheme = 0;
1052 ret->attr = 0;
1053 ret->max_token = 0;
1054 ret->data = NULL;
1055 ret->data_len = 0;
1056 ret->finished = FALSE;
1057 return ret;
1060 void destroy_authinfo( struct authinfo *authinfo )
1062 if (!authinfo) return;
1064 if (SecIsValidHandle( &authinfo->ctx ))
1065 DeleteSecurityContext( &authinfo->ctx );
1066 if (SecIsValidHandle( &authinfo->cred ))
1067 FreeCredentialsHandle( &authinfo->cred );
1069 free( authinfo->data );
1070 free( authinfo );
1073 static BOOL get_authvalue( struct request *request, DWORD level, DWORD scheme, WCHAR *buffer, DWORD len )
1075 DWORD size, index = 0;
1076 for (;;)
1078 size = len;
1079 if (query_headers( request, level, NULL, buffer, &size, &index )) return FALSE;
1080 if (auth_scheme_from_header( buffer ) == scheme) break;
1082 return TRUE;
1085 static BOOL do_authorization( struct request *request, DWORD target, DWORD scheme_flag )
1087 struct authinfo *authinfo, **auth_ptr;
1088 enum auth_scheme scheme = scheme_from_flag( scheme_flag );
1089 const WCHAR *auth_target, *username, *password;
1090 WCHAR auth_value[2048], *auth_reply;
1091 DWORD len = sizeof(auth_value), len_scheme, flags;
1092 BOOL ret, has_auth_value;
1094 if (scheme == SCHEME_INVALID) return FALSE;
1096 switch (target)
1098 case WINHTTP_AUTH_TARGET_SERVER:
1099 has_auth_value = get_authvalue( request, WINHTTP_QUERY_WWW_AUTHENTICATE, scheme_flag, auth_value, len );
1100 auth_ptr = &request->authinfo;
1101 auth_target = L"Authorization";
1102 if (request->creds[TARGET_SERVER][scheme].username)
1104 if (scheme != SCHEME_BASIC && !has_auth_value) return FALSE;
1105 username = request->creds[TARGET_SERVER][scheme].username;
1106 password = request->creds[TARGET_SERVER][scheme].password;
1108 else
1110 if (!has_auth_value) return FALSE;
1111 username = request->connect->username;
1112 password = request->connect->password;
1114 break;
1116 case WINHTTP_AUTH_TARGET_PROXY:
1117 if (!get_authvalue( request, WINHTTP_QUERY_PROXY_AUTHENTICATE, scheme_flag, auth_value, len ))
1118 return FALSE;
1119 auth_ptr = &request->proxy_authinfo;
1120 auth_target = L"Proxy-Authorization";
1121 if (request->creds[TARGET_PROXY][scheme].username)
1123 username = request->creds[TARGET_PROXY][scheme].username;
1124 password = request->creds[TARGET_PROXY][scheme].password;
1126 else
1128 username = request->connect->session->proxy_username;
1129 password = request->connect->session->proxy_password;
1131 break;
1133 default:
1134 WARN( "unknown target %#lx\n", target );
1135 return FALSE;
1137 authinfo = *auth_ptr;
1139 switch (scheme)
1141 case SCHEME_BASIC:
1143 int userlen, passlen;
1145 if (!username || !password) return FALSE;
1146 if ((!authinfo && !(authinfo = alloc_authinfo())) || authinfo->finished) return FALSE;
1148 userlen = WideCharToMultiByte( CP_UTF8, 0, username, lstrlenW( username ), NULL, 0, NULL, NULL );
1149 passlen = WideCharToMultiByte( CP_UTF8, 0, password, lstrlenW( password ), NULL, 0, NULL, NULL );
1151 authinfo->data_len = userlen + 1 + passlen;
1152 if (!(authinfo->data = malloc( authinfo->data_len ))) return FALSE;
1154 WideCharToMultiByte( CP_UTF8, 0, username, -1, authinfo->data, userlen, NULL, NULL );
1155 authinfo->data[userlen] = ':';
1156 WideCharToMultiByte( CP_UTF8, 0, password, -1, authinfo->data + userlen + 1, passlen, NULL, NULL );
1158 authinfo->scheme = SCHEME_BASIC;
1159 authinfo->finished = TRUE;
1160 break;
1162 case SCHEME_NTLM:
1163 case SCHEME_NEGOTIATE:
1165 SECURITY_STATUS status;
1166 SecBufferDesc out_desc, in_desc;
1167 SecBuffer out, in;
1168 ULONG flags = ISC_REQ_CONNECTION|ISC_REQ_USE_DCE_STYLE|ISC_REQ_MUTUAL_AUTH|ISC_REQ_DELEGATE;
1169 const WCHAR *p;
1170 BOOL first = FALSE;
1172 if (!authinfo)
1174 TimeStamp exp;
1175 SEC_WINNT_AUTH_IDENTITY_W id;
1176 WCHAR *domain, *user;
1178 if (!username || !password || !(authinfo = alloc_authinfo())) return FALSE;
1180 first = TRUE;
1181 domain = (WCHAR *)username;
1182 user = wcschr( username, '\\' );
1184 if (user) user++;
1185 else
1187 user = (WCHAR *)username;
1188 domain = NULL;
1190 id.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1191 id.User = user;
1192 id.UserLength = lstrlenW( user );
1193 id.Domain = domain;
1194 id.DomainLength = domain ? user - domain - 1 : 0;
1195 id.Password = (WCHAR *)password;
1196 id.PasswordLength = lstrlenW( password );
1198 status = AcquireCredentialsHandleW( NULL, (SEC_WCHAR *)auth_schemes[scheme].str,
1199 SECPKG_CRED_OUTBOUND, NULL, &id, NULL, NULL,
1200 &authinfo->cred, &exp );
1201 if (status == SEC_E_OK)
1203 PSecPkgInfoW info;
1204 status = QuerySecurityPackageInfoW( (SEC_WCHAR *)auth_schemes[scheme].str, &info );
1205 if (status == SEC_E_OK)
1207 authinfo->max_token = info->cbMaxToken;
1208 FreeContextBuffer( info );
1211 if (status != SEC_E_OK)
1213 WARN( "AcquireCredentialsHandleW for scheme %s failed with error %#lx\n",
1214 debugstr_w(auth_schemes[scheme].str), status );
1215 free( authinfo );
1216 return FALSE;
1218 authinfo->scheme = scheme;
1220 else if (authinfo->finished) return FALSE;
1222 if ((lstrlenW( auth_value ) < auth_schemes[authinfo->scheme].len ||
1223 wcsnicmp( auth_value, auth_schemes[authinfo->scheme].str, auth_schemes[authinfo->scheme].len )))
1225 ERR("authentication scheme changed from %s to %s\n",
1226 debugstr_w(auth_schemes[authinfo->scheme].str), debugstr_w(auth_value));
1227 destroy_authinfo( authinfo );
1228 *auth_ptr = NULL;
1229 return FALSE;
1231 in.BufferType = SECBUFFER_TOKEN;
1232 in.cbBuffer = 0;
1233 in.pvBuffer = NULL;
1235 in_desc.ulVersion = 0;
1236 in_desc.cBuffers = 1;
1237 in_desc.pBuffers = &in;
1239 p = auth_value + auth_schemes[scheme].len;
1240 if (*p == ' ')
1242 int len = lstrlenW( ++p );
1243 in.cbBuffer = decode_base64( p, len, NULL );
1244 if (!(in.pvBuffer = malloc( in.cbBuffer ))) {
1245 destroy_authinfo( authinfo );
1246 *auth_ptr = NULL;
1247 return FALSE;
1249 decode_base64( p, len, in.pvBuffer );
1251 out.BufferType = SECBUFFER_TOKEN;
1252 out.cbBuffer = authinfo->max_token;
1253 if (!(out.pvBuffer = malloc( authinfo->max_token )))
1255 free( in.pvBuffer );
1256 destroy_authinfo( authinfo );
1257 *auth_ptr = NULL;
1258 return FALSE;
1260 out_desc.ulVersion = 0;
1261 out_desc.cBuffers = 1;
1262 out_desc.pBuffers = &out;
1264 status = InitializeSecurityContextW( first ? &authinfo->cred : NULL, first ? NULL : &authinfo->ctx,
1265 first ? request->connect->servername : NULL, flags, 0,
1266 SECURITY_NETWORK_DREP, in.pvBuffer ? &in_desc : NULL, 0,
1267 &authinfo->ctx, &out_desc, &authinfo->attr, &authinfo->exp );
1268 free( in.pvBuffer );
1269 if (status == SEC_E_OK)
1271 free( authinfo->data );
1272 authinfo->data = out.pvBuffer;
1273 authinfo->data_len = out.cbBuffer;
1274 authinfo->finished = TRUE;
1275 TRACE("sending last auth packet\n");
1277 else if (status == SEC_I_CONTINUE_NEEDED)
1279 free( authinfo->data );
1280 authinfo->data = out.pvBuffer;
1281 authinfo->data_len = out.cbBuffer;
1282 TRACE("sending next auth packet\n");
1284 else
1286 ERR( "InitializeSecurityContextW failed with error %#lx\n", status );
1287 free( out.pvBuffer );
1288 destroy_authinfo( authinfo );
1289 *auth_ptr = NULL;
1290 return FALSE;
1292 break;
1294 default:
1295 ERR("invalid scheme %u\n", scheme);
1296 return FALSE;
1298 *auth_ptr = authinfo;
1300 len_scheme = auth_schemes[authinfo->scheme].len;
1301 len = len_scheme + 1 + ((authinfo->data_len + 2) * 4) / 3;
1302 if (!(auth_reply = malloc( (len + 1) * sizeof(WCHAR) ))) return FALSE;
1304 memcpy( auth_reply, auth_schemes[authinfo->scheme].str, len_scheme * sizeof(WCHAR) );
1305 auth_reply[len_scheme] = ' ';
1306 encode_base64( authinfo->data, authinfo->data_len, auth_reply + len_scheme + 1 );
1308 flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE;
1309 ret = !process_header( request, auth_target, auth_reply, flags, TRUE );
1310 free( auth_reply );
1311 return ret;
1314 static WCHAR *build_proxy_connect_string( struct request *request )
1316 WCHAR *ret, *host;
1317 unsigned int i;
1318 int len = lstrlenW( request->connect->hostname ) + 7;
1320 if (!(host = malloc( len * sizeof(WCHAR) ))) return NULL;
1321 len = swprintf( host, len, L"%s:%u", request->connect->hostname, request->connect->hostport );
1323 len += ARRAY_SIZE(L"CONNECT");
1324 len += ARRAY_SIZE(L"HTTP/1.1");
1326 for (i = 0; i < request->num_headers; i++)
1328 if (request->headers[i].is_request)
1329 len += lstrlenW( request->headers[i].field ) + lstrlenW( request->headers[i].value ) + 4; /* '\r\n: ' */
1331 len += 4; /* '\r\n\r\n' */
1333 if ((ret = malloc( (len + 1) * sizeof(WCHAR) )))
1335 lstrcpyW( ret, L"CONNECT" );
1336 lstrcatW( ret, L" " );
1337 lstrcatW( ret, host );
1338 lstrcatW( ret, L" " );
1339 lstrcatW( ret, L"HTTP/1.1" );
1341 for (i = 0; i < request->num_headers; i++)
1343 if (request->headers[i].is_request)
1345 lstrcatW( ret, L"\r\n" );
1346 lstrcatW( ret, request->headers[i].field );
1347 lstrcatW( ret, L": " );
1348 lstrcatW( ret, request->headers[i].value );
1351 lstrcatW( ret, L"\r\n\r\n" );
1354 free( host );
1355 return ret;
1358 static DWORD read_reply( struct request *request );
1360 static DWORD secure_proxy_connect( struct request *request )
1362 WCHAR *str;
1363 char *strA;
1364 int len, bytes_sent;
1365 DWORD ret;
1367 if (!(str = build_proxy_connect_string( request ))) return ERROR_OUTOFMEMORY;
1368 strA = strdupWA( str );
1369 free( str );
1370 if (!strA) return ERROR_OUTOFMEMORY;
1372 len = strlen( strA );
1373 ret = netconn_send( request->netconn, strA, len, &bytes_sent, NULL );
1374 free( strA );
1375 if (!ret) ret = read_reply( request );
1377 return ret;
1380 static WCHAR *addr_to_str( struct sockaddr_storage *addr )
1382 char buf[INET6_ADDRSTRLEN];
1383 void *src;
1385 switch (addr->ss_family)
1387 case AF_INET:
1388 src = &((struct sockaddr_in *)addr)->sin_addr;
1389 break;
1390 case AF_INET6:
1391 src = &((struct sockaddr_in6 *)addr)->sin6_addr;
1392 break;
1393 default:
1394 WARN("unsupported address family %d\n", addr->ss_family);
1395 return NULL;
1397 if (!inet_ntop( addr->ss_family, src, buf, sizeof(buf) )) return NULL;
1398 return strdupAW( buf );
1401 static CRITICAL_SECTION connection_pool_cs;
1402 static CRITICAL_SECTION_DEBUG connection_pool_debug =
1404 0, 0, &connection_pool_cs,
1405 { &connection_pool_debug.ProcessLocksList, &connection_pool_debug.ProcessLocksList },
1406 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
1408 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
1410 static struct list connection_pool = LIST_INIT( connection_pool );
1412 void release_host( struct hostdata *host )
1414 LONG ref;
1416 EnterCriticalSection( &connection_pool_cs );
1417 if (!(ref = --host->ref)) list_remove( &host->entry );
1418 LeaveCriticalSection( &connection_pool_cs );
1419 if (ref) return;
1421 assert( list_empty( &host->connections ) );
1422 free( host->hostname );
1423 free( host );
1426 static BOOL connection_collector_running;
1428 static void CALLBACK connection_collector( TP_CALLBACK_INSTANCE *instance, void *ctx )
1430 unsigned int remaining_connections;
1431 struct netconn *netconn, *next_netconn;
1432 struct hostdata *host, *next_host;
1433 ULONGLONG now;
1437 /* FIXME: Use more sophisticated method */
1438 Sleep(5000);
1439 remaining_connections = 0;
1440 now = GetTickCount64();
1442 EnterCriticalSection(&connection_pool_cs);
1444 LIST_FOR_EACH_ENTRY_SAFE(host, next_host, &connection_pool, struct hostdata, entry)
1446 LIST_FOR_EACH_ENTRY_SAFE(netconn, next_netconn, &host->connections, struct netconn, entry)
1448 if (netconn->keep_until < now)
1450 TRACE("freeing %p\n", netconn);
1451 list_remove(&netconn->entry);
1452 netconn_close(netconn);
1454 else remaining_connections++;
1458 if (!remaining_connections) connection_collector_running = FALSE;
1460 LeaveCriticalSection(&connection_pool_cs);
1461 } while(remaining_connections);
1463 FreeLibraryWhenCallbackReturns( instance, winhttp_instance );
1466 static void cache_connection( struct netconn *netconn )
1468 TRACE( "caching connection %p\n", netconn );
1470 EnterCriticalSection( &connection_pool_cs );
1472 netconn->keep_until = GetTickCount64() + DEFAULT_KEEP_ALIVE_TIMEOUT;
1473 list_add_head( &netconn->host->connections, &netconn->entry );
1475 if (!connection_collector_running)
1477 HMODULE module;
1479 GetModuleHandleExW( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR *)winhttp_instance, &module );
1481 if (TrySubmitThreadpoolCallback( connection_collector, NULL, NULL )) connection_collector_running = TRUE;
1482 else FreeLibrary( winhttp_instance );
1485 LeaveCriticalSection( &connection_pool_cs );
1488 static DWORD map_secure_protocols( DWORD mask )
1490 DWORD ret = 0;
1491 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_SSL2) ret |= SP_PROT_SSL2_CLIENT;
1492 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_SSL3) ret |= SP_PROT_SSL3_CLIENT;
1493 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1) ret |= SP_PROT_TLS1_CLIENT;
1494 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1) ret |= SP_PROT_TLS1_1_CLIENT;
1495 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2) ret |= SP_PROT_TLS1_2_CLIENT;
1496 return ret;
1499 static DWORD ensure_cred_handle( struct request *request )
1501 SECURITY_STATUS status = SEC_E_OK;
1503 if (request->cred_handle_initialized) return ERROR_SUCCESS;
1505 if (!request->cred_handle_initialized)
1507 SCHANNEL_CRED cred;
1508 memset( &cred, 0, sizeof(cred) );
1509 cred.dwVersion = SCHANNEL_CRED_VERSION;
1510 cred.grbitEnabledProtocols = map_secure_protocols( request->connect->session->secure_protocols );
1511 if (request->client_cert)
1513 cred.paCred = &request->client_cert;
1514 cred.cCreds = 1;
1516 status = AcquireCredentialsHandleW( NULL, (WCHAR *)UNISP_NAME_W, SECPKG_CRED_OUTBOUND, NULL,
1517 &cred, NULL, NULL, &request->cred_handle, NULL );
1518 if (status == SEC_E_OK)
1519 request->cred_handle_initialized = TRUE;
1522 if (status != SEC_E_OK)
1524 WARN( "AcquireCredentialsHandleW failed: %#lx\n", status );
1525 return status;
1527 return ERROR_SUCCESS;
1530 static DWORD open_connection( struct request *request )
1532 BOOL is_secure = request->hdr.flags & WINHTTP_FLAG_SECURE;
1533 struct hostdata *host = NULL, *iter;
1534 struct netconn *netconn = NULL;
1535 struct connect *connect;
1536 WCHAR *addressW = NULL;
1537 INTERNET_PORT port;
1538 DWORD ret, len;
1540 if (request->netconn) goto done;
1542 connect = request->connect;
1543 port = connect->serverport ? connect->serverport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80);
1545 EnterCriticalSection( &connection_pool_cs );
1547 LIST_FOR_EACH_ENTRY( iter, &connection_pool, struct hostdata, entry )
1549 if (iter->port == port && !wcscmp( connect->servername, iter->hostname ) && !is_secure == !iter->secure)
1551 host = iter;
1552 host->ref++;
1553 break;
1557 if (!host)
1559 if ((host = malloc( sizeof(*host) )))
1561 host->ref = 1;
1562 host->secure = is_secure;
1563 host->port = port;
1564 list_init( &host->connections );
1565 if ((host->hostname = strdupW( connect->servername )))
1567 list_add_head( &connection_pool, &host->entry );
1569 else
1571 free( host );
1572 host = NULL;
1577 LeaveCriticalSection( &connection_pool_cs );
1579 if (!host) return ERROR_OUTOFMEMORY;
1581 for (;;)
1583 EnterCriticalSection( &connection_pool_cs );
1584 if (!list_empty( &host->connections ))
1586 netconn = LIST_ENTRY( list_head( &host->connections ), struct netconn, entry );
1587 list_remove( &netconn->entry );
1589 LeaveCriticalSection( &connection_pool_cs );
1590 if (!netconn) break;
1592 if (netconn_is_alive( netconn )) break;
1593 TRACE("connection %p no longer alive, closing\n", netconn);
1594 netconn_close( netconn );
1595 netconn = NULL;
1598 if (!connect->resolved && netconn)
1600 connect->sockaddr = netconn->sockaddr;
1601 connect->resolved = TRUE;
1604 if (!connect->resolved)
1606 len = lstrlenW( host->hostname ) + 1;
1607 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, host->hostname, len );
1609 if ((ret = netconn_resolve( host->hostname, port, &connect->sockaddr, request->resolve_timeout )))
1611 release_host( host );
1612 return ret;
1614 connect->resolved = TRUE;
1616 if (!(addressW = addr_to_str( &connect->sockaddr )))
1618 release_host( host );
1619 return ERROR_OUTOFMEMORY;
1621 len = lstrlenW( addressW ) + 1;
1622 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, addressW, len );
1625 if (!netconn)
1627 if (!addressW && !(addressW = addr_to_str( &connect->sockaddr )))
1629 release_host( host );
1630 return ERROR_OUTOFMEMORY;
1633 TRACE("connecting to %s:%u\n", debugstr_w(addressW), port);
1635 len = lstrlenW( addressW ) + 1;
1636 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, addressW, len );
1638 if ((ret = netconn_create( host, &connect->sockaddr, request->connect_timeout, &netconn )))
1640 free( addressW );
1641 release_host( host );
1642 return ret;
1644 netconn_set_timeout( netconn, TRUE, request->send_timeout );
1645 netconn_set_timeout( netconn, FALSE, request->receive_response_timeout );
1647 request->netconn = netconn;
1649 if (is_secure)
1651 if (connect->session->proxy_server && wcsicmp( connect->hostname, connect->servername ))
1653 if ((ret = secure_proxy_connect( request )))
1655 request->netconn = NULL;
1656 free( addressW );
1657 netconn_close( netconn );
1658 return ret;
1662 CertFreeCertificateContext( request->server_cert );
1663 request->server_cert = NULL;
1665 if ((ret = ensure_cred_handle( request )) ||
1666 (ret = netconn_secure_connect( netconn, connect->hostname, request->security_flags,
1667 &request->cred_handle, request->check_revocation )))
1669 request->netconn = NULL;
1670 free( addressW );
1671 netconn_close( netconn );
1672 return ret;
1676 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, addressW, lstrlenW(addressW) + 1 );
1678 else
1680 TRACE("using connection %p\n", netconn);
1682 netconn_set_timeout( netconn, TRUE, request->send_timeout );
1683 netconn_set_timeout( netconn, FALSE, request->receive_response_timeout );
1684 request->netconn = netconn;
1687 if (netconn->secure && !(request->server_cert = netconn_get_certificate( netconn )))
1689 free( addressW );
1690 netconn_close( netconn );
1691 return ERROR_WINHTTP_SECURE_FAILURE;
1694 done:
1695 request->read_pos = request->read_size = 0;
1696 request->read_chunked = FALSE;
1697 request->read_chunked_size = ~0u;
1698 request->read_chunked_eof = FALSE;
1699 free( addressW );
1700 return ERROR_SUCCESS;
1703 void close_connection( struct request *request )
1705 if (!request->netconn) return;
1707 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, 0, 0 );
1708 netconn_close( request->netconn );
1709 request->netconn = NULL;
1710 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, 0, 0 );
1713 static DWORD add_host_header( struct request *request, DWORD modifier )
1715 DWORD ret, len;
1716 WCHAR *host;
1717 struct connect *connect = request->connect;
1718 INTERNET_PORT port;
1720 port = connect->hostport ? connect->hostport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80);
1722 if (port == INTERNET_DEFAULT_HTTP_PORT || port == INTERNET_DEFAULT_HTTPS_PORT)
1724 return process_header( request, L"Host", connect->hostname, modifier, TRUE );
1726 len = lstrlenW( connect->hostname ) + 7; /* sizeof(":65335") */
1727 if (!(host = malloc( len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
1728 swprintf( host, len, L"%s:%u", connect->hostname, port );
1729 ret = process_header( request, L"Host", host, modifier, TRUE );
1730 free( host );
1731 return ret;
1734 static void clear_response_headers( struct request *request )
1736 unsigned int i;
1738 for (i = 0; i < request->num_headers; i++)
1740 if (!request->headers[i].field) continue;
1741 if (!request->headers[i].value) continue;
1742 if (request->headers[i].is_request) continue;
1743 delete_header( request, i );
1744 i--;
1748 /* remove some amount of data from the read buffer */
1749 static void remove_data( struct request *request, int count )
1751 if (!(request->read_size -= count)) request->read_pos = 0;
1752 else request->read_pos += count;
1755 /* read some more data into the read buffer */
1756 static DWORD read_more_data( struct request *request, int maxlen, BOOL notify )
1758 int len;
1759 DWORD ret;
1761 if (request->read_chunked_eof) return ERROR_INSUFFICIENT_BUFFER;
1763 if (request->read_size && request->read_pos)
1765 /* move existing data to the start of the buffer */
1766 memmove( request->read_buf, request->read_buf + request->read_pos, request->read_size );
1767 request->read_pos = 0;
1769 if (maxlen == -1) maxlen = sizeof(request->read_buf);
1771 if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 );
1773 ret = netconn_recv( request->netconn, request->read_buf + request->read_size,
1774 maxlen - request->read_size, 0, &len );
1776 if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, &len, sizeof(len) );
1778 request->read_size += len;
1779 return ret;
1782 /* discard data contents until we reach end of line */
1783 static DWORD discard_eol( struct request *request, BOOL notify )
1785 DWORD ret;
1788 char *eol = memchr( request->read_buf + request->read_pos, '\n', request->read_size );
1789 if (eol)
1791 remove_data( request, (eol + 1) - (request->read_buf + request->read_pos) );
1792 break;
1794 request->read_pos = request->read_size = 0; /* discard everything */
1795 if ((ret = read_more_data( request, -1, notify ))) return ret;
1796 } while (request->read_size);
1797 return ERROR_SUCCESS;
1800 /* read the size of the next chunk */
1801 static DWORD start_next_chunk( struct request *request, BOOL notify )
1803 DWORD ret, chunk_size = 0;
1805 assert(!request->read_chunked_size || request->read_chunked_size == ~0u);
1807 if (request->read_chunked_eof) return ERROR_INSUFFICIENT_BUFFER;
1809 /* read terminator for the previous chunk */
1810 if (!request->read_chunked_size && (ret = discard_eol( request, notify ))) return ret;
1812 for (;;)
1814 while (request->read_size)
1816 char ch = request->read_buf[request->read_pos];
1817 if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
1818 else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
1819 else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
1820 else if (ch == ';' || ch == '\r' || ch == '\n')
1822 TRACE( "reading %lu byte chunk\n", chunk_size );
1824 if (request->content_length == ~0u) request->content_length = chunk_size;
1825 else request->content_length += chunk_size;
1827 request->read_chunked_size = chunk_size;
1828 if (!chunk_size) request->read_chunked_eof = TRUE;
1830 return discard_eol( request, notify );
1832 remove_data( request, 1 );
1834 if ((ret = read_more_data( request, -1, notify ))) return ret;
1835 if (!request->read_size)
1837 request->content_length = request->content_read = 0;
1838 request->read_chunked_size = 0;
1839 return ERROR_SUCCESS;
1844 static DWORD refill_buffer( struct request *request, BOOL notify )
1846 int len = sizeof(request->read_buf);
1847 DWORD ret;
1849 if (request->read_chunked)
1851 if (request->read_chunked_eof) return ERROR_INSUFFICIENT_BUFFER;
1852 if (request->read_chunked_size == ~0u || !request->read_chunked_size)
1854 if ((ret = start_next_chunk( request, notify ))) return ret;
1856 len = min( len, request->read_chunked_size );
1858 else if (request->content_length != ~0u)
1860 len = min( len, request->content_length - request->content_read );
1863 if (len <= request->read_size) return ERROR_SUCCESS;
1864 if ((ret = read_more_data( request, len, notify ))) return ret;
1865 if (!request->read_size) request->content_length = request->content_read = 0;
1866 return ERROR_SUCCESS;
1869 static void finished_reading( struct request *request )
1871 BOOL close = FALSE;
1872 WCHAR connection[20];
1873 DWORD size = sizeof(connection);
1875 if (!request->netconn) return;
1877 if (request->netconn->socket == -1) close = TRUE;
1878 else if (request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE) close = TRUE;
1879 else if (!query_headers( request, WINHTTP_QUERY_CONNECTION, NULL, connection, &size, NULL ) ||
1880 !query_headers( request, WINHTTP_QUERY_PROXY_CONNECTION, NULL, connection, &size, NULL ))
1882 if (!wcsicmp( connection, L"close" )) close = TRUE;
1884 else if (!wcscmp( request->version, L"HTTP/1.0" )) close = TRUE;
1886 if (close)
1887 netconn_close( request->netconn );
1888 else
1889 cache_connection( request->netconn );
1890 request->netconn = NULL;
1893 /* return the size of data available to be read immediately */
1894 static DWORD get_available_data( struct request *request )
1896 if (request->read_chunked) return min( request->read_chunked_size, request->read_size );
1897 return request->read_size;
1900 /* check if we have reached the end of the data to read */
1901 static BOOL end_of_read_data( struct request *request )
1903 if (!request->content_length) return TRUE;
1904 if (request->read_chunked) return request->read_chunked_eof;
1905 if (request->content_length == ~0u) return FALSE;
1906 return (request->content_length == request->content_read);
1909 static DWORD read_data( struct request *request, void *buffer, DWORD size, DWORD *read, BOOL async )
1911 int count, bytes_read = 0;
1912 DWORD ret = ERROR_SUCCESS;
1914 if (end_of_read_data( request )) goto done;
1916 while (size)
1918 if (!(count = get_available_data( request )))
1920 if ((ret = refill_buffer( request, async ))) goto done;
1921 if (!(count = get_available_data( request ))) goto done;
1923 count = min( count, size );
1924 memcpy( (char *)buffer + bytes_read, request->read_buf + request->read_pos, count );
1925 remove_data( request, count );
1926 if (request->read_chunked) request->read_chunked_size -= count;
1927 size -= count;
1928 bytes_read += count;
1929 request->content_read += count;
1930 if (end_of_read_data( request )) goto done;
1932 if (request->read_chunked && !request->read_chunked_size) ret = refill_buffer( request, async );
1934 done:
1935 TRACE( "retrieved %u bytes (%lu/%lu)\n", bytes_read, request->content_read, request->content_length );
1936 if (end_of_read_data( request )) finished_reading( request );
1937 if (async)
1939 if (!ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, buffer, bytes_read );
1940 else
1942 WINHTTP_ASYNC_RESULT result;
1943 result.dwResult = API_READ_DATA;
1944 result.dwError = ret;
1945 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
1949 if (!ret && read) *read = bytes_read;
1950 return ret;
1953 /* read any content returned by the server so that the connection can be reused */
1954 static void drain_content( struct request *request )
1956 DWORD size, bytes_read, bytes_total = 0, bytes_left = request->content_length - request->content_read;
1957 char buffer[2048];
1959 refill_buffer( request, FALSE );
1960 for (;;)
1962 if (request->read_chunked) size = sizeof(buffer);
1963 else size = min( sizeof(buffer), bytes_left - bytes_total );
1965 if (read_data( request, buffer, size, &bytes_read, FALSE ) || !bytes_read) return;
1966 bytes_total += bytes_read;
1970 enum escape_flags
1972 ESCAPE_FLAG_NON_PRINTABLE = 0x01,
1973 ESCAPE_FLAG_SPACE = 0x02,
1974 ESCAPE_FLAG_PERCENT = 0x04,
1975 ESCAPE_FLAG_UNSAFE = 0x08,
1976 ESCAPE_FLAG_DEL = 0x10,
1977 ESCAPE_FLAG_8BIT = 0x20,
1978 ESCAPE_FLAG_STRIP_CRLF = 0x40,
1981 #define ESCAPE_MASK_DEFAULT (ESCAPE_FLAG_NON_PRINTABLE | ESCAPE_FLAG_SPACE | ESCAPE_FLAG_UNSAFE |\
1982 ESCAPE_FLAG_DEL | ESCAPE_FLAG_8BIT)
1983 #define ESCAPE_MASK_PERCENT (ESCAPE_FLAG_PERCENT | ESCAPE_MASK_DEFAULT)
1984 #define ESCAPE_MASK_DISABLE (ESCAPE_FLAG_SPACE | ESCAPE_FLAG_8BIT | ESCAPE_FLAG_STRIP_CRLF)
1986 static inline BOOL need_escape( char ch, enum escape_flags flags )
1988 static const char unsafe[] = "\"#<>[\\]^`{|}";
1989 const char *ptr = unsafe;
1991 if ((flags & ESCAPE_FLAG_SPACE) && ch == ' ') return TRUE;
1992 if ((flags & ESCAPE_FLAG_PERCENT) && ch == '%') return TRUE;
1993 if ((flags & ESCAPE_FLAG_NON_PRINTABLE) && ch < 0x20) return TRUE;
1994 if ((flags & ESCAPE_FLAG_DEL) && ch == 0x7f) return TRUE;
1995 if ((flags & ESCAPE_FLAG_8BIT) && (ch & 0x80)) return TRUE;
1996 if ((flags & ESCAPE_FLAG_UNSAFE)) while (*ptr) { if (ch == *ptr++) return TRUE; }
1997 return FALSE;
2000 static DWORD escape_string( const char *src, DWORD len, char *dst, enum escape_flags flags )
2002 static const char hex[] = "0123456789ABCDEF";
2003 DWORD i, ret = len;
2004 char *ptr = dst;
2006 for (i = 0; i < len; i++)
2008 if ((flags & ESCAPE_FLAG_STRIP_CRLF) && (src[i] == '\r' || src[i] == '\n'))
2010 ret--;
2011 continue;
2013 if (need_escape( src[i], flags ))
2015 if (dst)
2017 ptr[0] = '%';
2018 ptr[1] = hex[(src[i] >> 4) & 0xf];
2019 ptr[2] = hex[src[i] & 0xf];
2020 ptr += 3;
2022 ret += 2;
2024 else if (dst) *ptr++ = src[i];
2027 if (dst) dst[ret] = 0;
2028 return ret;
2031 static DWORD str_to_wire( const WCHAR *src, int src_len, char *dst, enum escape_flags flags )
2033 DWORD len;
2034 char *utf8;
2036 if (src_len < 0) src_len = lstrlenW( src );
2037 len = WideCharToMultiByte( CP_UTF8, 0, src, src_len, NULL, 0, NULL, NULL );
2038 if (!(utf8 = malloc( len ))) return 0;
2040 WideCharToMultiByte( CP_UTF8, 0, src, -1, utf8, len, NULL, NULL );
2041 len = escape_string( utf8, len, dst, flags );
2042 free( utf8 );
2044 return len;
2047 static char *build_wire_path( struct request *request, DWORD *ret_len )
2049 WCHAR *full_path;
2050 const WCHAR *start, *path, *query = NULL;
2051 DWORD len, len_path = 0, len_query = 0;
2052 enum escape_flags path_flags, query_flags;
2053 char *ret;
2055 if (!wcsicmp( request->connect->hostname, request->connect->servername )) start = full_path = request->path;
2056 else if (!(full_path = build_absolute_request_path( request, &start ))) return NULL;
2058 len = lstrlenW( full_path );
2059 if ((path = wcschr( start, '/' )))
2061 len_path = lstrlenW( path );
2062 if ((query = wcschr( path, '?' )))
2064 len_query = lstrlenW( query );
2065 len_path -= len_query;
2069 if (request->hdr.flags & WINHTTP_FLAG_ESCAPE_DISABLE) path_flags = ESCAPE_MASK_DISABLE;
2070 else if (request->hdr.flags & WINHTTP_FLAG_ESCAPE_PERCENT) path_flags = ESCAPE_MASK_PERCENT;
2071 else path_flags = ESCAPE_MASK_DEFAULT;
2073 if (request->hdr.flags & WINHTTP_FLAG_ESCAPE_DISABLE_QUERY) query_flags = ESCAPE_MASK_DISABLE;
2074 else query_flags = path_flags;
2076 *ret_len = str_to_wire( full_path, len - len_path - len_query, NULL, 0 );
2077 if (path) *ret_len += str_to_wire( path, len_path, NULL, path_flags );
2078 if (query) *ret_len += str_to_wire( query, len_query, NULL, query_flags );
2080 if ((ret = malloc( *ret_len + 1 )))
2082 len = str_to_wire( full_path, len - len_path - len_query, ret, 0 );
2083 if (path) len += str_to_wire( path, len_path, ret + len, path_flags );
2084 if (query) str_to_wire( query, len_query, ret + len, query_flags );
2087 if (full_path != request->path) free( full_path );
2088 return ret;
2091 static char *build_wire_request( struct request *request, DWORD *len )
2093 char *path, *ptr, *ret;
2094 DWORD i, len_path;
2096 if (!(path = build_wire_path( request, &len_path ))) return NULL;
2098 *len = str_to_wire( request->verb, -1, NULL, 0 ) + 1; /* ' ' */
2099 *len += len_path + 1; /* ' ' */
2100 *len += str_to_wire( request->version, -1, NULL, 0 );
2102 for (i = 0; i < request->num_headers; i++)
2104 if (request->headers[i].is_request)
2106 *len += str_to_wire( request->headers[i].field, -1, NULL, 0 ) + 2; /* ': ' */
2107 *len += str_to_wire( request->headers[i].value, -1, NULL, 0 ) + 2; /* '\r\n' */
2110 *len += 4; /* '\r\n\r\n' */
2112 if ((ret = ptr = malloc( *len + 1 )))
2114 ptr += str_to_wire( request->verb, -1, ptr, 0 );
2115 *ptr++ = ' ';
2116 memcpy( ptr, path, len_path );
2117 ptr += len_path;
2118 *ptr++ = ' ';
2119 ptr += str_to_wire( request->version, -1, ptr, 0 );
2121 for (i = 0; i < request->num_headers; i++)
2123 if (request->headers[i].is_request)
2125 *ptr++ = '\r';
2126 *ptr++ = '\n';
2127 ptr += str_to_wire( request->headers[i].field, -1, ptr, 0 );
2128 *ptr++ = ':';
2129 *ptr++ = ' ';
2130 ptr += str_to_wire( request->headers[i].value, -1, ptr, 0 );
2133 memcpy( ptr, "\r\n\r\n", sizeof("\r\n\r\n") );
2136 free( path );
2137 return ret;
2140 static WCHAR *create_websocket_key(void)
2142 WCHAR *ret;
2143 char buf[16];
2144 DWORD base64_len = ((sizeof(buf) + 2) * 4) / 3;
2145 if (!RtlGenRandom( buf, sizeof(buf) )) return NULL;
2146 if ((ret = malloc( (base64_len + 1) * sizeof(WCHAR) ))) encode_base64( buf, sizeof(buf), ret );
2147 return ret;
2150 static DWORD add_websocket_key_header( struct request *request )
2152 WCHAR *key = create_websocket_key();
2153 if (!key) return ERROR_OUTOFMEMORY;
2154 process_header( request, L"Sec-WebSocket-Key", key, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE, TRUE );
2155 free( key );
2156 return ERROR_SUCCESS;
2159 static DWORD send_request( struct request *request, const WCHAR *headers, DWORD headers_len, void *optional,
2160 DWORD optional_len, DWORD total_len, DWORD_PTR context, BOOL async )
2162 struct connect *connect = request->connect;
2163 struct session *session = connect->session;
2164 char *wire_req;
2165 int bytes_sent;
2166 DWORD ret, len;
2168 drain_content( request );
2169 clear_response_headers( request );
2171 if (session->agent)
2172 process_header( request, L"User-Agent", session->agent, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2174 if (connect->hostname)
2175 add_host_header( request, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW );
2177 if (request->creds[TARGET_SERVER][SCHEME_BASIC].username)
2178 do_authorization( request, WINHTTP_AUTH_TARGET_SERVER, WINHTTP_AUTH_SCHEME_BASIC );
2180 if (total_len || (request->verb && !wcscmp( request->verb, L"POST" )))
2182 WCHAR length[21]; /* decimal long int + null */
2183 swprintf( length, ARRAY_SIZE(length), L"%ld", total_len );
2184 process_header( request, L"Content-Length", length, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2186 if (request->flags & REQUEST_FLAG_WEBSOCKET_UPGRADE)
2188 process_header( request, L"Upgrade", L"websocket", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2189 process_header( request, L"Connection", L"Upgrade", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2190 process_header( request, L"Sec-WebSocket-Version", L"13", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2191 if ((ret = add_websocket_key_header( request ))) return ret;
2193 else if (!(request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE))
2195 process_header( request, L"Connection", L"Keep-Alive", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2197 if (request->hdr.flags & WINHTTP_FLAG_REFRESH)
2199 process_header( request, L"Pragma", L"no-cache", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2200 process_header( request, L"Cache-Control", L"no-cache", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2202 if (headers && (ret = add_request_headers( request, headers, headers_len,
2203 WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE )))
2205 TRACE( "failed to add request headers: %lu\n", ret );
2206 return ret;
2208 if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES) && (ret = add_cookie_headers( request )))
2210 WARN( "failed to add cookie headers: %lu\n", ret );
2211 return ret;
2214 if (context) request->hdr.context = context;
2216 if ((ret = open_connection( request ))) goto end;
2217 if (!(wire_req = build_wire_request( request, &len )))
2219 ret = ERROR_OUTOFMEMORY;
2220 goto end;
2222 TRACE("full request: %s\n", debugstr_a(wire_req));
2224 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, NULL, 0 );
2226 ret = netconn_send( request->netconn, wire_req, len, &bytes_sent, NULL );
2227 free( wire_req );
2228 if (ret) goto end;
2230 if (optional_len)
2232 if ((ret = netconn_send( request->netconn, optional, optional_len, &bytes_sent, NULL ))) goto end;
2233 request->optional = optional;
2234 request->optional_len = optional_len;
2235 len += optional_len;
2237 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, &len, sizeof(len) );
2239 end:
2240 if (async)
2242 if (!ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, NULL, 0 );
2243 else
2245 WINHTTP_ASYNC_RESULT result;
2246 result.dwResult = API_SEND_REQUEST;
2247 result.dwError = ret;
2248 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
2251 return ret;
2254 static void task_send_request( void *ctx, BOOL abort )
2256 struct send_request *s = ctx;
2257 struct request *request = (struct request *)s->task_hdr.obj;
2259 if (abort) return;
2261 TRACE( "running %p\n", ctx );
2262 send_request( request, s->headers, s->headers_len, s->optional, s->optional_len, s->total_len, s->context, TRUE );
2264 free( s->headers );
2267 /***********************************************************************
2268 * WinHttpSendRequest (winhttp.@)
2270 BOOL WINAPI WinHttpSendRequest( HINTERNET hrequest, const WCHAR *headers, DWORD headers_len,
2271 void *optional, DWORD optional_len, DWORD total_len, DWORD_PTR context )
2273 DWORD ret;
2274 struct request *request;
2276 TRACE( "%p, %s, %lu, %p, %lu, %lu, %Ix\n", hrequest, debugstr_wn(headers, headers_len), headers_len, optional,
2277 optional_len, total_len, context );
2279 if (!(request = (struct request *)grab_object( hrequest )))
2281 SetLastError( ERROR_INVALID_HANDLE );
2282 return FALSE;
2284 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2286 release_object( &request->hdr );
2287 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2288 return FALSE;
2291 if (headers && !headers_len) headers_len = lstrlenW( headers );
2293 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
2295 struct send_request *s;
2297 if (!(s = malloc( sizeof(*s) )))
2299 release_object( &request->hdr );
2300 SetLastError( ERROR_OUTOFMEMORY );
2301 return FALSE;
2303 s->headers = strdupW( headers );
2304 s->headers_len = headers_len;
2305 s->optional = optional;
2306 s->optional_len = optional_len;
2307 s->total_len = total_len;
2308 s->context = context;
2310 if ((ret = queue_task( &request->queue, task_send_request, &s->task_hdr, &request->hdr )))
2312 free( s->headers );
2313 free( s );
2316 else ret = send_request( request, headers, headers_len, optional, optional_len, total_len, context, FALSE );
2318 release_object( &request->hdr );
2319 SetLastError( ret );
2320 return !ret;
2323 static DWORD set_credentials( struct request *request, DWORD target, DWORD scheme_flag, const WCHAR *username,
2324 const WCHAR *password )
2326 enum auth_scheme scheme = scheme_from_flag( scheme_flag );
2328 if (scheme == SCHEME_INVALID || ((scheme == SCHEME_BASIC || scheme == SCHEME_DIGEST) && (!username || !password)))
2330 return ERROR_INVALID_PARAMETER;
2332 switch (target)
2334 case WINHTTP_AUTH_TARGET_SERVER:
2336 free( request->creds[TARGET_SERVER][scheme].username );
2337 if (!username) request->creds[TARGET_SERVER][scheme].username = NULL;
2338 else if (!(request->creds[TARGET_SERVER][scheme].username = strdupW( username ))) return ERROR_OUTOFMEMORY;
2340 free( request->creds[TARGET_SERVER][scheme].password );
2341 if (!password) request->creds[TARGET_SERVER][scheme].password = NULL;
2342 else if (!(request->creds[TARGET_SERVER][scheme].password = strdupW( password ))) return ERROR_OUTOFMEMORY;
2343 break;
2345 case WINHTTP_AUTH_TARGET_PROXY:
2347 free( request->creds[TARGET_PROXY][scheme].username );
2348 if (!username) request->creds[TARGET_PROXY][scheme].username = NULL;
2349 else if (!(request->creds[TARGET_PROXY][scheme].username = strdupW( username ))) return ERROR_OUTOFMEMORY;
2351 free( request->creds[TARGET_PROXY][scheme].password );
2352 if (!password) request->creds[TARGET_PROXY][scheme].password = NULL;
2353 else if (!(request->creds[TARGET_PROXY][scheme].password = strdupW( password ))) return ERROR_OUTOFMEMORY;
2354 break;
2356 default:
2357 WARN( "unknown target %lu\n", target );
2358 return ERROR_INVALID_PARAMETER;
2360 return ERROR_SUCCESS;
2363 /***********************************************************************
2364 * WinHttpSetCredentials (winhttp.@)
2366 BOOL WINAPI WinHttpSetCredentials( HINTERNET hrequest, DWORD target, DWORD scheme, const WCHAR *username,
2367 const WCHAR *password, void *params )
2369 DWORD ret;
2370 struct request *request;
2372 TRACE( "%p, %lu, %#lx, %s, %p, %p\n", hrequest, target, scheme, debugstr_w(username), password, params );
2374 if (!(request = (struct request *)grab_object( hrequest )))
2376 SetLastError( ERROR_INVALID_HANDLE );
2377 return FALSE;
2379 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2381 release_object( &request->hdr );
2382 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2383 return FALSE;
2386 ret = set_credentials( request, target, scheme, username, password );
2388 release_object( &request->hdr );
2389 SetLastError( ret );
2390 return !ret;
2393 static DWORD handle_authorization( struct request *request, DWORD status )
2395 DWORD ret, i, schemes, first, level, target;
2397 switch (status)
2399 case HTTP_STATUS_DENIED:
2400 target = WINHTTP_AUTH_TARGET_SERVER;
2401 level = WINHTTP_QUERY_WWW_AUTHENTICATE;
2402 break;
2404 case HTTP_STATUS_PROXY_AUTH_REQ:
2405 target = WINHTTP_AUTH_TARGET_PROXY;
2406 level = WINHTTP_QUERY_PROXY_AUTHENTICATE;
2407 break;
2409 default:
2410 ERR( "unhandled status %lu\n", status );
2411 return ERROR_WINHTTP_INTERNAL_ERROR;
2414 if ((ret = query_auth_schemes( request, level, &schemes, &first ))) return ret;
2415 if (do_authorization( request, target, first )) return ERROR_SUCCESS;
2417 schemes &= ~first;
2418 for (i = 0; i < ARRAY_SIZE( auth_schemes ); i++)
2420 if (!(schemes & auth_schemes[i].scheme)) continue;
2421 if (do_authorization( request, target, auth_schemes[i].scheme )) return ERROR_SUCCESS;
2423 return ERROR_WINHTTP_LOGIN_FAILURE;
2426 /* set the request content length based on the headers */
2427 static void set_content_length( struct request *request, DWORD status )
2429 WCHAR encoding[20];
2430 DWORD buflen = sizeof(request->content_length);
2432 if (status == HTTP_STATUS_NO_CONTENT || status == HTTP_STATUS_NOT_MODIFIED ||
2433 status == HTTP_STATUS_SWITCH_PROTOCOLS || !wcscmp( request->verb, L"HEAD" ))
2435 request->content_length = 0;
2437 else
2439 if (query_headers( request, WINHTTP_QUERY_CONTENT_LENGTH|WINHTTP_QUERY_FLAG_NUMBER,
2440 NULL, &request->content_length, &buflen, NULL ))
2441 request->content_length = ~0u;
2443 buflen = sizeof(encoding);
2444 if (!query_headers( request, WINHTTP_QUERY_TRANSFER_ENCODING, NULL, encoding, &buflen, NULL ) &&
2445 !wcsicmp( encoding, L"chunked" ))
2447 request->content_length = ~0u;
2448 request->read_chunked = TRUE;
2449 request->read_chunked_size = ~0u;
2450 request->read_chunked_eof = FALSE;
2453 request->content_read = 0;
2456 static DWORD read_line( struct request *request, char *buffer, DWORD *len )
2458 int count, bytes_read, pos = 0;
2459 DWORD ret;
2461 for (;;)
2463 char *eol = memchr( request->read_buf + request->read_pos, '\n', request->read_size );
2464 if (eol)
2466 count = eol - (request->read_buf + request->read_pos);
2467 bytes_read = count + 1;
2469 else count = bytes_read = request->read_size;
2471 count = min( count, *len - pos );
2472 memcpy( buffer + pos, request->read_buf + request->read_pos, count );
2473 pos += count;
2474 remove_data( request, bytes_read );
2475 if (eol) break;
2477 if ((ret = read_more_data( request, -1, TRUE ))) return ret;
2478 if (!request->read_size)
2480 *len = 0;
2481 TRACE("returning empty string\n");
2482 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
2485 if (pos < *len)
2487 if (pos && buffer[pos - 1] == '\r') pos--;
2488 *len = pos + 1;
2490 buffer[*len - 1] = 0;
2491 TRACE("returning %s\n", debugstr_a(buffer));
2492 return ERROR_SUCCESS;
2495 #define MAX_REPLY_LEN 1460
2496 #define INITIAL_HEADER_BUFFER_LEN 512
2498 static DWORD read_reply( struct request *request )
2500 char buffer[MAX_REPLY_LEN];
2501 DWORD ret, buflen, len, offset, crlf_len = 2; /* lstrlenW(crlf) */
2502 char *status_code, *status_text;
2503 WCHAR *versionW, *status_textW, *raw_headers;
2504 WCHAR status_codeW[4]; /* sizeof("nnn") */
2506 if (!request->netconn) return ERROR_WINHTTP_INCORRECT_HANDLE_STATE;
2510 buflen = MAX_REPLY_LEN;
2511 if ((ret = read_line( request, buffer, &buflen ))) return ret;
2513 /* first line should look like 'HTTP/1.x nnn OK' where nnn is the status code */
2514 if (!(status_code = strchr( buffer, ' ' ))) return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
2515 status_code++;
2516 if (!(status_text = strchr( status_code, ' ' ))) return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
2517 if ((len = status_text - status_code) != sizeof("nnn") - 1) return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
2518 status_text++;
2520 TRACE("version [%s] status code [%s] status text [%s]\n",
2521 debugstr_an(buffer, status_code - buffer - 1),
2522 debugstr_an(status_code, len),
2523 debugstr_a(status_text));
2525 } while (!memcmp( status_code, "100", len )); /* ignore "100 Continue" responses */
2527 /* we rely on the fact that the protocol is ascii */
2528 MultiByteToWideChar( CP_ACP, 0, status_code, len, status_codeW, len );
2529 status_codeW[len] = 0;
2530 if ((ret = process_header( request, L"Status", status_codeW,
2531 WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE, FALSE ))) return ret;
2533 len = status_code - buffer;
2534 if (!(versionW = malloc( len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
2535 MultiByteToWideChar( CP_ACP, 0, buffer, len - 1, versionW, len -1 );
2536 versionW[len - 1] = 0;
2538 free( request->version );
2539 request->version = versionW;
2541 len = buflen - (status_text - buffer);
2542 if (!(status_textW = malloc( len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
2543 MultiByteToWideChar( CP_ACP, 0, status_text, len, status_textW, len );
2545 free( request->status_text );
2546 request->status_text = status_textW;
2548 len = max( buflen + crlf_len, INITIAL_HEADER_BUFFER_LEN );
2549 if (!(raw_headers = malloc( len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
2550 MultiByteToWideChar( CP_ACP, 0, buffer, buflen, raw_headers, buflen );
2551 memcpy( raw_headers + buflen - 1, L"\r\n", sizeof(L"\r\n") );
2553 free( request->raw_headers );
2554 request->raw_headers = raw_headers;
2556 offset = buflen + crlf_len - 1;
2557 for (;;)
2559 struct header *header;
2560 int lenW;
2562 buflen = MAX_REPLY_LEN;
2563 if (read_line( request, buffer, &buflen )) return ERROR_SUCCESS;
2564 if (!*buffer) buflen = 1;
2566 while (len - offset < buflen + crlf_len)
2568 WCHAR *tmp;
2569 len *= 2;
2570 if (!(tmp = realloc( raw_headers, len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
2571 request->raw_headers = raw_headers = tmp;
2573 if (!*buffer)
2575 memcpy( raw_headers + offset, L"\r\n", sizeof(L"\r\n") );
2576 break;
2578 lenW = MultiByteToWideChar( CP_ACP, 0, buffer, buflen, raw_headers + offset, buflen );
2580 if (!(header = parse_header( raw_headers + offset, lenW - 1 ))) break;
2581 if ((ret = process_header( request, header->field, header->value, WINHTTP_ADDREQ_FLAG_ADD, FALSE )))
2583 free_header( header );
2584 break;
2586 free_header( header );
2587 memcpy( raw_headers + offset + buflen - 1, L"\r\n", sizeof(L"\r\n") );
2588 offset += buflen + crlf_len - 1;
2591 TRACE("raw headers: %s\n", debugstr_w(raw_headers));
2592 return ret;
2595 static void record_cookies( struct request *request )
2597 unsigned int i;
2599 for (i = 0; i < request->num_headers; i++)
2601 struct header *set_cookie = &request->headers[i];
2602 if (!wcsicmp( set_cookie->field, L"Set-Cookie" ) && !set_cookie->is_request)
2604 set_cookies( request, set_cookie->value );
2609 static DWORD get_redirect_url( struct request *request, WCHAR **ret_url, DWORD *ret_len )
2611 DWORD size, ret;
2612 WCHAR *url;
2614 ret = query_headers( request, WINHTTP_QUERY_LOCATION, NULL, NULL, &size, NULL );
2615 if (ret != ERROR_INSUFFICIENT_BUFFER) return ret;
2616 if (!(url = malloc( size ))) return ERROR_OUTOFMEMORY;
2617 if ((ret = query_headers( request, WINHTTP_QUERY_LOCATION, NULL, url, &size, NULL )))
2619 free( url );
2620 return ret;
2622 *ret_url = url;
2623 *ret_len = size / sizeof(WCHAR);
2624 return ERROR_SUCCESS;
2627 static DWORD handle_redirect( struct request *request, DWORD status )
2629 DWORD ret, len, len_loc = 0;
2630 URL_COMPONENTS uc;
2631 struct connect *connect = request->connect;
2632 INTERNET_PORT port;
2633 WCHAR *hostname = NULL, *location = NULL;
2635 if ((ret = get_redirect_url( request, &location, &len_loc ))) return ret;
2637 memset( &uc, 0, sizeof(uc) );
2638 uc.dwStructSize = sizeof(uc);
2639 uc.dwSchemeLength = uc.dwHostNameLength = uc.dwUrlPathLength = uc.dwExtraInfoLength = ~0u;
2641 if (!WinHttpCrackUrl( location, len_loc, 0, &uc )) /* assume relative redirect */
2643 WCHAR *path, *p;
2645 ret = ERROR_OUTOFMEMORY;
2646 if (location[0] == '/')
2648 if (!(path = malloc( (len_loc + 1) * sizeof(WCHAR) ))) goto end;
2649 memcpy( path, location, len_loc * sizeof(WCHAR) );
2650 path[len_loc] = 0;
2652 else
2654 if ((p = wcsrchr( request->path, '/' ))) *p = 0;
2655 len = lstrlenW( request->path ) + 1 + len_loc;
2656 if (!(path = malloc( (len + 1) * sizeof(WCHAR) ))) goto end;
2657 lstrcpyW( path, request->path );
2658 lstrcatW( path, L"/" );
2659 memcpy( path + lstrlenW(path), location, len_loc * sizeof(WCHAR) );
2660 path[len_loc] = 0;
2662 free( request->path );
2663 request->path = path;
2664 ret = ERROR_SUCCESS;
2666 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, len_loc + 1 );
2668 else
2670 if (uc.nScheme == INTERNET_SCHEME_HTTP && request->hdr.flags & WINHTTP_FLAG_SECURE)
2672 if (request->hdr.redirect_policy == WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP)
2674 ret = ERROR_WINHTTP_REDIRECT_FAILED;
2675 goto end;
2677 TRACE("redirect from secure page to non-secure page\n");
2678 request->hdr.flags &= ~WINHTTP_FLAG_SECURE;
2680 else if (uc.nScheme == INTERNET_SCHEME_HTTPS && !(request->hdr.flags & WINHTTP_FLAG_SECURE))
2682 TRACE("redirect from non-secure page to secure page\n");
2683 request->hdr.flags |= WINHTTP_FLAG_SECURE;
2686 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, len_loc + 1 );
2688 len = uc.dwHostNameLength;
2689 if (!(hostname = malloc( (len + 1) * sizeof(WCHAR) )))
2691 ret = ERROR_OUTOFMEMORY;
2692 goto end;
2694 memcpy( hostname, uc.lpszHostName, len * sizeof(WCHAR) );
2695 hostname[len] = 0;
2697 port = uc.nPort ? uc.nPort : (uc.nScheme == INTERNET_SCHEME_HTTPS ? 443 : 80);
2698 if (wcsicmp( connect->hostname, hostname ) || connect->serverport != port)
2700 free( connect->hostname );
2701 connect->hostname = hostname;
2702 connect->hostport = port;
2703 if (!set_server_for_hostname( connect, hostname, port ))
2705 ret = ERROR_OUTOFMEMORY;
2706 goto end;
2709 netconn_close( request->netconn );
2710 request->netconn = NULL;
2711 request->content_length = request->content_read = 0;
2712 request->read_pos = request->read_size = 0;
2713 request->read_chunked = request->read_chunked_eof = FALSE;
2715 else free( hostname );
2717 if ((ret = add_host_header( request, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE ))) goto end;
2718 if ((ret = open_connection( request ))) goto end;
2720 free( request->path );
2721 request->path = NULL;
2722 if (uc.dwUrlPathLength)
2724 len = uc.dwUrlPathLength + uc.dwExtraInfoLength;
2725 if (!(request->path = malloc( (len + 1) * sizeof(WCHAR) ))) goto end;
2726 memcpy( request->path, uc.lpszUrlPath, (len + 1) * sizeof(WCHAR) );
2727 request->path[len] = 0;
2729 else request->path = strdupW( L"/" );
2732 if (status != HTTP_STATUS_REDIRECT_KEEP_VERB && !wcscmp( request->verb, L"POST" ))
2734 free( request->verb );
2735 request->verb = strdupW( L"GET" );
2736 request->optional = NULL;
2737 request->optional_len = 0;
2740 end:
2741 free( location );
2742 return ret;
2745 static BOOL is_passport_request( struct request *request )
2747 static const WCHAR passportW[] = {'P','a','s','s','p','o','r','t','1','.','4'};
2748 WCHAR buf[1024];
2749 DWORD len = ARRAY_SIZE(buf);
2751 if (!(request->connect->session->passport_flags & WINHTTP_ENABLE_PASSPORT_AUTH) ||
2752 query_headers( request, WINHTTP_QUERY_WWW_AUTHENTICATE, NULL, buf, &len, NULL )) return FALSE;
2754 if (!wcsnicmp( buf, passportW, ARRAY_SIZE(passportW) ) &&
2755 (buf[ARRAY_SIZE(passportW)] == ' ' || !buf[ARRAY_SIZE(passportW)])) return TRUE;
2757 return FALSE;
2760 static DWORD handle_passport_redirect( struct request *request )
2762 DWORD ret, flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE;
2763 int i, len = lstrlenW( request->raw_headers );
2764 WCHAR *p = request->raw_headers;
2766 if ((ret = process_header( request, L"Status", L"401", flags, FALSE ))) return ret;
2768 for (i = 0; i < len; i++)
2770 if (i <= len - 3 && p[i] == '3' && p[i + 1] == '0' && p[i + 2] == '2')
2772 p[i] = '4';
2773 p[i + 2] = '1';
2774 break;
2777 return ERROR_SUCCESS;
2780 static DWORD receive_response( struct request *request, BOOL async )
2782 DWORD ret, size, query, status;
2784 if (!request->netconn) return ERROR_WINHTTP_INCORRECT_HANDLE_STATE;
2786 netconn_set_timeout( request->netconn, FALSE, request->receive_response_timeout );
2787 for (;;)
2789 if ((ret = read_reply( request ))) break;
2791 size = sizeof(DWORD);
2792 query = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER;
2793 if ((ret = query_headers( request, query, NULL, &status, &size, NULL ))) break;
2795 set_content_length( request, status );
2797 if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES)) record_cookies( request );
2799 if (status == HTTP_STATUS_REDIRECT && is_passport_request( request ))
2801 ret = handle_passport_redirect( request );
2803 else if (status == HTTP_STATUS_MOVED || status == HTTP_STATUS_REDIRECT || status == HTTP_STATUS_REDIRECT_KEEP_VERB)
2805 if (request->hdr.disable_flags & WINHTTP_DISABLE_REDIRECTS ||
2806 request->hdr.redirect_policy == WINHTTP_OPTION_REDIRECT_POLICY_NEVER) break;
2808 if (++request->redirect_count > request->max_redirects) return ERROR_WINHTTP_REDIRECT_FAILED;
2810 if ((ret = handle_redirect( request, status ))) break;
2812 /* recurse synchronously */
2813 if (!(ret = send_request( request, NULL, 0, request->optional, request->optional_len, 0, 0, FALSE ))) continue;
2815 else if (status == HTTP_STATUS_DENIED || status == HTTP_STATUS_PROXY_AUTH_REQ)
2817 if (request->hdr.disable_flags & WINHTTP_DISABLE_AUTHENTICATION) break;
2819 if (handle_authorization( request, status )) break;
2821 /* recurse synchronously */
2822 if (!(ret = send_request( request, NULL, 0, request->optional, request->optional_len, 0, 0, FALSE ))) continue;
2824 break;
2827 if (request->netconn) netconn_set_timeout( request->netconn, FALSE, request->receive_timeout );
2828 if (request->content_length) ret = refill_buffer( request, FALSE );
2830 if (async)
2832 if (!ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, NULL, 0 );
2833 else
2835 WINHTTP_ASYNC_RESULT result;
2836 result.dwResult = API_RECEIVE_RESPONSE;
2837 result.dwError = ret;
2838 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
2841 return ret;
2844 static void task_receive_response( void *ctx, BOOL abort )
2846 struct receive_response *r = ctx;
2847 struct request *request = (struct request *)r->task_hdr.obj;
2849 if (abort) return;
2851 TRACE("running %p\n", ctx);
2852 receive_response( request, TRUE );
2855 /***********************************************************************
2856 * WinHttpReceiveResponse (winhttp.@)
2858 BOOL WINAPI WinHttpReceiveResponse( HINTERNET hrequest, LPVOID reserved )
2860 DWORD ret;
2861 struct request *request;
2863 TRACE("%p, %p\n", hrequest, reserved);
2865 if (!(request = (struct request *)grab_object( hrequest )))
2867 SetLastError( ERROR_INVALID_HANDLE );
2868 return FALSE;
2870 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2872 release_object( &request->hdr );
2873 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2874 return FALSE;
2877 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
2879 struct receive_response *r;
2881 if (!(r = malloc( sizeof(*r) )))
2883 release_object( &request->hdr );
2884 SetLastError( ERROR_OUTOFMEMORY );
2885 return FALSE;
2887 if ((ret = queue_task( &request->queue, task_receive_response, &r->task_hdr, &request->hdr )))
2888 free( r );
2890 else ret = receive_response( request, FALSE );
2892 release_object( &request->hdr );
2893 SetLastError( ret );
2894 return !ret;
2897 static DWORD query_data_ready( struct request *request )
2899 DWORD count;
2901 count = get_available_data( request );
2902 if (!request->read_chunked && request->netconn) count += netconn_query_data_available( request->netconn );
2904 return count;
2907 static BOOL skip_async_queue( struct request *request )
2909 return request->hdr.recursion_count < 3 && (end_of_read_data( request ) || query_data_ready( request ));
2912 static DWORD query_data_available( struct request *request, DWORD *available, BOOL async )
2914 DWORD ret = ERROR_SUCCESS, count = 0;
2916 if (end_of_read_data( request )) goto done;
2918 if (!(count = query_data_ready( request )))
2920 if ((ret = refill_buffer( request, async ))) goto done;
2921 count = query_data_ready( request );
2924 done:
2925 TRACE( "%lu bytes available\n", count );
2926 if (async)
2928 if (!ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, &count, sizeof(count) );
2929 else
2931 WINHTTP_ASYNC_RESULT result;
2932 result.dwResult = API_QUERY_DATA_AVAILABLE;
2933 result.dwError = ret;
2934 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
2938 if (!ret && available) *available = count;
2939 return ret;
2942 static void task_query_data_available( void *ctx, BOOL abort )
2944 struct query_data *q = ctx;
2945 struct request *request = (struct request *)q->task_hdr.obj;
2947 if (abort) return;
2949 TRACE("running %p\n", ctx);
2950 query_data_available( request, q->available, TRUE );
2953 /***********************************************************************
2954 * WinHttpQueryDataAvailable (winhttp.@)
2956 BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available )
2958 DWORD ret;
2959 struct request *request;
2960 BOOL async;
2962 TRACE("%p, %p\n", hrequest, available);
2964 if (!(request = (struct request *)grab_object( hrequest )))
2966 SetLastError( ERROR_INVALID_HANDLE );
2967 return FALSE;
2969 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2971 release_object( &request->hdr );
2972 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2973 return FALSE;
2976 if ((async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) && !skip_async_queue( request ))
2978 struct query_data *q;
2980 if (!(q = malloc( sizeof(*q) )))
2982 release_object( &request->hdr );
2983 SetLastError( ERROR_OUTOFMEMORY );
2984 return FALSE;
2987 q->available = available;
2989 if ((ret = queue_task( &request->queue, task_query_data_available, &q->task_hdr, &request->hdr )))
2990 free( q );
2991 else
2992 ret = ERROR_IO_PENDING;
2994 else ret = query_data_available( request, available, async );
2996 release_object( &request->hdr );
2997 SetLastError( ret );
2998 return !ret || ret == ERROR_IO_PENDING;
3001 static void task_read_data( void *ctx, BOOL abort )
3003 struct read_data *r = ctx;
3004 struct request *request = (struct request *)r->task_hdr.obj;
3006 if (abort) return;
3008 TRACE("running %p\n", ctx);
3009 read_data( request, r->buffer, r->to_read, r->read, TRUE );
3012 /***********************************************************************
3013 * WinHttpReadData (winhttp.@)
3015 BOOL WINAPI WinHttpReadData( HINTERNET hrequest, void *buffer, DWORD to_read, DWORD *read )
3017 DWORD ret;
3018 struct request *request;
3019 BOOL async;
3021 TRACE( "%p, %p, %lu, %p\n", hrequest, buffer, to_read, read );
3023 if (!(request = (struct request *)grab_object( hrequest )))
3025 SetLastError( ERROR_INVALID_HANDLE );
3026 return FALSE;
3028 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
3030 release_object( &request->hdr );
3031 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
3032 return FALSE;
3035 if ((async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) && !skip_async_queue( request ))
3037 struct read_data *r;
3039 if (!(r = malloc( sizeof(*r) )))
3041 release_object( &request->hdr );
3042 SetLastError( ERROR_OUTOFMEMORY );
3043 return FALSE;
3045 r->buffer = buffer;
3046 r->to_read = to_read;
3047 r->read = read;
3049 if ((ret = queue_task( &request->queue, task_read_data, &r->task_hdr, &request->hdr )))
3050 free( r );
3051 else
3052 ret = ERROR_IO_PENDING;
3054 else ret = read_data( request, buffer, to_read, read, async );
3056 release_object( &request->hdr );
3057 SetLastError( ret );
3058 return !ret || ret == ERROR_IO_PENDING;
3061 static DWORD write_data( struct request *request, const void *buffer, DWORD to_write, DWORD *written, BOOL async )
3063 DWORD ret;
3064 int num_bytes;
3066 ret = netconn_send( request->netconn, buffer, to_write, &num_bytes, NULL );
3068 if (async)
3070 if (!ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, &num_bytes, sizeof(num_bytes) );
3071 else
3073 WINHTTP_ASYNC_RESULT result;
3074 result.dwResult = API_WRITE_DATA;
3075 result.dwError = ret;
3076 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
3079 if (!ret && written) *written = num_bytes;
3080 return ret;
3083 static void task_write_data( void *ctx, BOOL abort )
3085 struct write_data *w = ctx;
3086 struct request *request = (struct request *)w->task_hdr.obj;
3088 if (abort) return;
3090 TRACE("running %p\n", ctx);
3091 write_data( request, w->buffer, w->to_write, w->written, TRUE );
3094 /***********************************************************************
3095 * WinHttpWriteData (winhttp.@)
3097 BOOL WINAPI WinHttpWriteData( HINTERNET hrequest, const void *buffer, DWORD to_write, DWORD *written )
3099 DWORD ret;
3100 struct request *request;
3102 TRACE( "%p, %p, %lu, %p\n", hrequest, buffer, to_write, written );
3104 if (!(request = (struct request *)grab_object( hrequest )))
3106 SetLastError( ERROR_INVALID_HANDLE );
3107 return FALSE;
3109 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
3111 release_object( &request->hdr );
3112 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
3113 return FALSE;
3116 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
3118 struct write_data *w;
3120 if (!(w = malloc( sizeof(*w) )))
3122 release_object( &request->hdr );
3123 SetLastError( ERROR_OUTOFMEMORY );
3124 return FALSE;
3126 w->buffer = buffer;
3127 w->to_write = to_write;
3128 w->written = written;
3130 if ((ret = queue_task( &request->queue, task_write_data, &w->task_hdr, &request->hdr )))
3131 free( w );
3133 else ret = write_data( request, buffer, to_write, written, FALSE );
3135 release_object( &request->hdr );
3136 SetLastError( ret );
3137 return !ret;
3140 static void socket_handle_closing( struct object_header *hdr )
3142 struct socket *socket = (struct socket *)hdr;
3143 BOOL pending_tasks;
3145 pending_tasks = cancel_queue( &socket->send_q );
3146 pending_tasks = cancel_queue( &socket->recv_q ) || pending_tasks;
3148 if (pending_tasks)
3149 netconn_cancel_io( socket->request->netconn );
3152 static BOOL socket_query_option( struct object_header *hdr, DWORD option, void *buffer, DWORD *buflen )
3154 switch (option)
3156 case WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL:
3157 SetLastError( ERROR_INVALID_PARAMETER );
3158 return FALSE;
3161 FIXME( "unimplemented option %lu\n", option );
3162 SetLastError( ERROR_WINHTTP_INVALID_OPTION );
3163 return FALSE;
3166 static void socket_destroy( struct object_header *hdr )
3168 struct socket *socket = (struct socket *)hdr;
3170 TRACE("%p\n", socket);
3172 stop_queue( &socket->send_q );
3173 stop_queue( &socket->recv_q );
3175 release_object( &socket->request->hdr );
3176 free( socket->send_frame_buffer );
3177 free( socket );
3180 static BOOL socket_set_option( struct object_header *hdr, DWORD option, void *buffer, DWORD buflen )
3182 struct socket *socket = (struct socket *)hdr;
3184 switch (option)
3186 case WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL:
3188 DWORD interval;
3190 if (buflen != sizeof(DWORD) || (interval = *(DWORD *)buffer) < 15000)
3192 WARN( "Invalid parameters for WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL.\n" );
3193 SetLastError( ERROR_INVALID_PARAMETER );
3194 return FALSE;
3196 socket->keepalive_interval = interval;
3197 netconn_set_timeout( socket->request->netconn, FALSE, socket->keepalive_interval );
3198 SetLastError( ERROR_SUCCESS );
3199 TRACE( "WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL %lu.\n", interval);
3200 return TRUE;
3204 FIXME( "unimplemented option %lu\n", option );
3205 SetLastError( ERROR_WINHTTP_INVALID_OPTION );
3206 return FALSE;
3209 static const struct object_vtbl socket_vtbl =
3211 socket_handle_closing,
3212 socket_destroy,
3213 socket_query_option,
3214 socket_set_option,
3217 HINTERNET WINAPI WinHttpWebSocketCompleteUpgrade( HINTERNET hrequest, DWORD_PTR context )
3219 struct socket *socket;
3220 struct request *request;
3221 HINTERNET hsocket = NULL;
3223 TRACE( "%p, %Ix\n", hrequest, context );
3225 if (!(request = (struct request *)grab_object( hrequest )))
3227 SetLastError( ERROR_INVALID_HANDLE );
3228 return NULL;
3230 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
3232 release_object( &request->hdr );
3233 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
3234 return NULL;
3236 if (!(socket = calloc( 1, sizeof(*socket) )))
3238 release_object( &request->hdr );
3239 return NULL;
3241 socket->hdr.type = WINHTTP_HANDLE_TYPE_SOCKET;
3242 socket->hdr.vtbl = &socket_vtbl;
3243 socket->hdr.refs = 1;
3244 socket->hdr.callback = request->hdr.callback;
3245 socket->hdr.notify_mask = request->hdr.notify_mask;
3246 socket->hdr.context = context;
3247 socket->keepalive_interval = 30000;
3248 InitializeSRWLock( &socket->send_lock );
3249 init_queue( &socket->send_q );
3250 init_queue( &socket->recv_q );
3252 addref_object( &request->hdr );
3253 socket->request = request;
3255 netconn_set_timeout( socket->request->netconn, FALSE, socket->keepalive_interval );
3257 if ((hsocket = alloc_handle( &socket->hdr )))
3259 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, &hsocket, sizeof(hsocket) );
3262 release_object( &socket->hdr );
3263 release_object( &request->hdr );
3264 TRACE("returning %p\n", hsocket);
3265 if (hsocket) SetLastError( ERROR_SUCCESS );
3266 return hsocket;
3269 static DWORD send_bytes( struct socket *socket, char *bytes, int len, int *sent, WSAOVERLAPPED *ovr )
3271 int count;
3272 DWORD err;
3273 err = netconn_send( socket->request->netconn, bytes, len, &count, ovr );
3274 if (sent) *sent = count;
3275 if (err) return err;
3276 return (count == len || (ovr && count)) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR;
3279 #define FIN_BIT (1 << 7)
3280 #define MASK_BIT (1 << 7)
3281 #define RESERVED_BIT (7 << 4)
3282 #define CONTROL_BIT (1 << 3)
3284 static DWORD send_frame( struct socket *socket, enum socket_opcode opcode, USHORT status, const char *buf,
3285 DWORD buflen, BOOL final, WSAOVERLAPPED *ovr )
3287 DWORD i, offset = 2, len = buflen, buffer_size, ret = 0;
3288 int sent_size;
3289 char hdr[14];
3290 char *ptr;
3292 TRACE( "sending %02x frame, len %lu\n", opcode, len );
3294 if (opcode == SOCKET_OPCODE_CLOSE) len += sizeof(status);
3296 hdr[0] = final ? (char)FIN_BIT : 0;
3297 hdr[0] |= opcode;
3298 hdr[1] = (char)MASK_BIT;
3299 if (len < 126) hdr[1] |= len;
3300 else if (len < 65536)
3302 hdr[1] |= 126;
3303 hdr[2] = len >> 8;
3304 hdr[3] = len & 0xff;
3305 offset += 2;
3307 else
3309 hdr[1] |= 127;
3310 hdr[2] = hdr[3] = hdr[4] = hdr[5] = 0;
3311 hdr[6] = len >> 24;
3312 hdr[7] = (len >> 16) & 0xff;
3313 hdr[8] = (len >> 8) & 0xff;
3314 hdr[9] = len & 0xff;
3315 offset += 8;
3318 buffer_size = len + offset + 4;
3319 assert( buffer_size - len < MAX_FRAME_BUFFER_SIZE );
3320 if (buffer_size > socket->send_frame_buffer_size && socket->send_frame_buffer_size < MAX_FRAME_BUFFER_SIZE)
3322 DWORD new_size;
3323 void *new;
3325 new_size = min( buffer_size, MAX_FRAME_BUFFER_SIZE );
3326 if (!(new = realloc( socket->send_frame_buffer, new_size )))
3328 ERR( "out of memory, buffer_size %lu\n", buffer_size);
3329 return ERROR_OUTOFMEMORY;
3331 socket->send_frame_buffer = new;
3332 socket->send_frame_buffer_size = new_size;
3334 ptr = socket->send_frame_buffer;
3336 memcpy(ptr, hdr, offset);
3337 ptr += offset;
3339 RtlGenRandom( socket->mask, 4 );
3340 memcpy( ptr, socket->mask, 4 );
3341 ptr += 4;
3342 socket->mask_index = 0;
3344 if (opcode == SOCKET_OPCODE_CLOSE) /* prepend status code */
3346 *ptr++ = (status >> 8) ^ socket->mask[socket->mask_index++ % 4];
3347 *ptr++ = (status & 0xff) ^ socket->mask[socket->mask_index++ % 4];
3350 offset = ptr - socket->send_frame_buffer;
3351 socket->send_remaining_size = offset + buflen;
3352 socket->client_buffer_offset = 0;
3353 while (socket->send_remaining_size)
3355 len = min( buflen, MAX_FRAME_BUFFER_SIZE - offset );
3356 for (i = 0; i < len; ++i)
3358 socket->send_frame_buffer[offset++] = buf[socket->client_buffer_offset++]
3359 ^ socket->mask[socket->mask_index++ % 4];
3362 sent_size = 0;
3363 ret = send_bytes( socket, socket->send_frame_buffer, offset, &sent_size, ovr );
3364 socket->send_remaining_size -= sent_size;
3365 if (ret)
3367 if (ovr && ret == WSA_IO_PENDING)
3369 memmove( socket->send_frame_buffer, socket->send_frame_buffer + sent_size, offset - sent_size );
3370 socket->bytes_in_send_frame_buffer = offset - sent_size;
3372 return ret;
3374 assert( sent_size == offset );
3375 offset = 0;
3376 buflen -= len;
3378 return ERROR_SUCCESS;
3381 static DWORD complete_send_frame( struct socket *socket, WSAOVERLAPPED *ovr, const char *buf )
3383 DWORD ret, len, i;
3385 if (!netconn_wait_overlapped_result( socket->request->netconn, ovr, &len ))
3386 return WSAGetLastError();
3388 if (socket->bytes_in_send_frame_buffer)
3390 ret = send_bytes( socket, socket->send_frame_buffer, socket->bytes_in_send_frame_buffer, NULL, NULL );
3391 if (ret) return ret;
3394 assert( socket->bytes_in_send_frame_buffer <= socket->send_remaining_size );
3395 socket->send_remaining_size -= socket->bytes_in_send_frame_buffer;
3397 while (socket->send_remaining_size)
3399 len = min( socket->send_remaining_size, MAX_FRAME_BUFFER_SIZE );
3400 for (i = 0; i < len; ++i)
3402 socket->send_frame_buffer[i] = buf[socket->client_buffer_offset++]
3403 ^ socket->mask[socket->mask_index++ % 4];
3405 ret = send_bytes( socket, socket->send_frame_buffer, len, NULL, NULL );
3406 if (ret) return ret;
3407 socket->send_remaining_size -= len;
3409 return ERROR_SUCCESS;
3412 static void send_io_complete( struct object_header *hdr )
3414 LONG count = InterlockedDecrement( &hdr->pending_sends );
3415 assert( count >= 0 );
3418 /* returns FALSE if sending callback should be omitted. */
3419 static void receive_io_complete( struct socket *socket )
3421 LONG count = InterlockedDecrement( &socket->hdr.pending_receives );
3422 assert( count >= 0 );
3425 static BOOL socket_can_send( struct socket *socket )
3427 return socket->state == SOCKET_STATE_OPEN && !socket->close_frame_received;
3430 static BOOL socket_can_receive( struct socket *socket )
3432 return socket->state <= SOCKET_STATE_SHUTDOWN && !socket->close_frame_received;
3435 static BOOL validate_buffer_type( WINHTTP_WEB_SOCKET_BUFFER_TYPE type, enum fragment_type current_fragment )
3437 switch (current_fragment)
3439 case SOCKET_FRAGMENT_NONE:
3440 return type == WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE
3441 || type == WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE
3442 || type == WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE
3443 || type == WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE;
3444 case SOCKET_FRAGMENT_BINARY:
3445 return type == WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE
3446 || type == WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE;
3447 case SOCKET_FRAGMENT_UTF8:
3448 return type == WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE
3449 || type == WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE;
3451 assert( 0 );
3452 return FALSE;
3455 static enum socket_opcode map_buffer_type( struct socket *socket, WINHTTP_WEB_SOCKET_BUFFER_TYPE type )
3457 switch (type)
3459 case WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE:
3460 if (socket->sending_fragment_type)
3462 socket->sending_fragment_type = SOCKET_FRAGMENT_NONE;
3463 return SOCKET_OPCODE_CONTINUE;
3465 return SOCKET_OPCODE_TEXT;
3467 case WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE:
3468 if (socket->sending_fragment_type)
3470 socket->sending_fragment_type = SOCKET_FRAGMENT_NONE;
3471 return SOCKET_OPCODE_CONTINUE;
3473 return SOCKET_OPCODE_BINARY;
3475 case WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE:
3476 if (!socket->sending_fragment_type)
3478 socket->sending_fragment_type = SOCKET_FRAGMENT_UTF8;
3479 return SOCKET_OPCODE_TEXT;
3481 return SOCKET_OPCODE_CONTINUE;
3483 case WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE:
3484 if (!socket->sending_fragment_type)
3486 socket->sending_fragment_type = SOCKET_FRAGMENT_BINARY;
3487 return SOCKET_OPCODE_BINARY;
3489 return SOCKET_OPCODE_CONTINUE;
3491 case WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE:
3492 return SOCKET_OPCODE_CLOSE;
3494 default:
3495 FIXME("buffer type %u not supported\n", type);
3496 return SOCKET_OPCODE_INVALID;
3500 static void socket_send_complete( struct socket *socket, DWORD ret, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, DWORD len )
3502 if (!ret)
3504 WINHTTP_WEB_SOCKET_STATUS status;
3505 status.dwBytesTransferred = len;
3506 status.eBufferType = type;
3507 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, &status, sizeof(status) );
3509 else
3511 WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
3512 result.AsyncResult.dwResult = API_WRITE_DATA;
3513 result.AsyncResult.dwError = ret;
3514 result.Operation = WINHTTP_WEB_SOCKET_SEND_OPERATION;
3515 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
3519 static DWORD socket_send( struct socket *socket, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, const void *buf, DWORD len,
3520 WSAOVERLAPPED *ovr )
3522 enum socket_opcode opcode = map_buffer_type( socket, type );
3523 BOOL final = (type != WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE &&
3524 type != WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE);
3526 return send_frame( socket, opcode, 0, buf, len, final, ovr );
3529 static void task_socket_send( void *ctx, BOOL abort )
3531 struct socket_send *s = ctx;
3532 struct socket *socket = (struct socket *)s->task_hdr.obj;
3533 DWORD ret;
3535 if (abort) return;
3537 TRACE("running %p\n", ctx);
3539 if (s->complete_async) ret = complete_send_frame( socket, &s->ovr, s->buf );
3540 else ret = socket_send( socket, s->type, s->buf, s->len, NULL );
3542 send_io_complete( &socket->hdr );
3543 InterlockedExchange( &socket->pending_noncontrol_send, 0 );
3544 socket_send_complete( socket, ret, s->type, s->len );
3547 DWORD WINAPI WinHttpWebSocketSend( HINTERNET hsocket, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, void *buf, DWORD len )
3549 struct socket *socket;
3550 DWORD ret = 0;
3552 TRACE( "%p, %u, %p, %lu\n", hsocket, type, buf, len );
3554 if (len && !buf) return ERROR_INVALID_PARAMETER;
3556 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
3557 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
3559 release_object( &socket->hdr );
3560 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
3562 if (!socket_can_send( socket ))
3564 release_object( &socket->hdr );
3565 return ERROR_INVALID_OPERATION;
3568 if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
3570 BOOL async_send, complete_async = FALSE;
3571 struct socket_send *s;
3573 if (InterlockedCompareExchange( &socket->pending_noncontrol_send, 1, 0 ))
3575 WARN( "Previous send is still queued.\n" );
3576 release_object( &socket->hdr );
3577 return ERROR_INVALID_OPERATION;
3579 if (!validate_buffer_type( type, socket->sending_fragment_type ))
3581 WARN( "Invalid buffer type %u, sending_fragment_type %u.\n", type, socket->sending_fragment_type );
3582 InterlockedExchange( &socket->pending_noncontrol_send, 0 );
3583 release_object( &socket->hdr );
3584 return ERROR_INVALID_PARAMETER;
3587 if (!(s = malloc( sizeof(*s) )))
3589 InterlockedExchange( &socket->pending_noncontrol_send, 0 );
3590 release_object( &socket->hdr );
3591 return ERROR_OUTOFMEMORY;
3594 AcquireSRWLockExclusive( &socket->send_lock );
3595 async_send = InterlockedIncrement( &socket->hdr.pending_sends ) > 1 || socket->hdr.recursion_count >= 3;
3596 if (!async_send)
3598 memset( &s->ovr, 0, sizeof(s->ovr) );
3599 if ((ret = socket_send( socket, type, buf, len, &s->ovr )) == WSA_IO_PENDING)
3601 async_send = TRUE;
3602 complete_async = TRUE;
3606 if (async_send)
3608 s->complete_async = complete_async;
3609 TRACE("queueing, complete_async %#x.\n", complete_async);
3610 s->type = type;
3611 s->buf = buf;
3612 s->len = len;
3614 if ((ret = queue_task( &socket->send_q, task_socket_send, &s->task_hdr, &socket->hdr )))
3615 free( s );
3617 if (!async_send || ret)
3619 InterlockedDecrement( &socket->hdr.pending_sends );
3620 InterlockedExchange( &socket->pending_noncontrol_send, 0 );
3622 ReleaseSRWLockExclusive( &socket->send_lock );
3623 if (!async_send)
3625 TRACE("sent sync.\n");
3626 free( s );
3627 socket_send_complete( socket, ret, type, len );
3628 ret = ERROR_SUCCESS;
3631 else
3633 if (validate_buffer_type( type, socket->sending_fragment_type ))
3635 ret = socket_send( socket, type, buf, len, NULL );
3637 else
3639 WARN( "Invalid buffer type %u, sending_fragment_type %u.\n", type, socket->sending_fragment_type );
3640 ret = ERROR_INVALID_PARAMETER;
3644 release_object( &socket->hdr );
3645 return ret;
3648 static DWORD receive_bytes( struct socket *socket, char *buf, DWORD len, DWORD *ret_len, BOOL read_full_buffer )
3650 DWORD err, size = 0, needed = len;
3651 char *ptr = buf;
3652 int received;
3654 if (socket->request->read_size)
3656 size = min( needed, socket->request->read_size );
3657 memcpy( ptr, socket->request->read_buf + socket->request->read_pos, size );
3658 remove_data( socket->request, size );
3659 needed -= size;
3660 ptr += size;
3662 while (size != len)
3664 if ((err = netconn_recv( socket->request->netconn, ptr, needed, 0, &received ))) return err;
3665 if (!received) break;
3666 size += received;
3667 if (!read_full_buffer) break;
3668 needed -= received;
3669 ptr += received;
3671 *ret_len = size;
3672 if (size != len && (read_full_buffer || !size)) return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
3673 return ERROR_SUCCESS;
3676 static BOOL is_supported_opcode( enum socket_opcode opcode )
3678 switch (opcode)
3680 case SOCKET_OPCODE_CONTINUE:
3681 case SOCKET_OPCODE_TEXT:
3682 case SOCKET_OPCODE_BINARY:
3683 case SOCKET_OPCODE_CLOSE:
3684 case SOCKET_OPCODE_PING:
3685 case SOCKET_OPCODE_PONG:
3686 return TRUE;
3687 default:
3688 FIXME( "opcode %02x not handled\n", opcode );
3689 return FALSE;
3693 static DWORD receive_frame( struct socket *socket, DWORD *ret_len, enum socket_opcode *opcode, BOOL *final )
3695 DWORD ret, len, count;
3696 char hdr[2];
3698 if ((ret = receive_bytes( socket, hdr, sizeof(hdr), &count, TRUE ))) return ret;
3699 if ((hdr[0] & RESERVED_BIT) || (hdr[1] & MASK_BIT) || !is_supported_opcode( hdr[0] & 0xf ))
3701 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
3703 *opcode = hdr[0] & 0xf;
3704 *final = hdr[0] & FIN_BIT;
3705 TRACE("received %02x frame, final %#x\n", *opcode, *final);
3707 len = hdr[1] & ~MASK_BIT;
3708 if (len == 126)
3710 USHORT len16;
3711 if ((ret = receive_bytes( socket, (char *)&len16, sizeof(len16), &count, TRUE ))) return ret;
3712 len = RtlUshortByteSwap( len16 );
3714 else if (len == 127)
3716 ULONGLONG len64;
3717 if ((ret = receive_bytes( socket, (char *)&len64, sizeof(len64), &count, TRUE ))) return ret;
3718 if ((len64 = RtlUlonglongByteSwap( len64 )) > ~0u) return ERROR_NOT_SUPPORTED;
3719 len = len64;
3722 *ret_len = len;
3723 return ERROR_SUCCESS;
3726 static void task_socket_send_pong( void *ctx, BOOL abort )
3728 struct socket_send *s = ctx;
3729 struct socket *socket = (struct socket *)s->task_hdr.obj;
3731 if (abort) return;
3733 TRACE("running %p\n", ctx);
3735 if (s->complete_async) complete_send_frame( socket, &s->ovr, NULL );
3736 else send_frame( socket, SOCKET_OPCODE_PONG, 0, NULL, 0, TRUE, NULL );
3738 send_io_complete( &socket->hdr );
3741 static DWORD socket_send_pong( struct socket *socket )
3743 if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
3745 BOOL async_send, complete_async = FALSE;
3746 struct socket_send *s;
3747 DWORD ret = 0;
3749 if (!(s = malloc( sizeof(*s) ))) return ERROR_OUTOFMEMORY;
3751 AcquireSRWLockExclusive( &socket->send_lock );
3752 async_send = InterlockedIncrement( &socket->hdr.pending_sends ) > 1;
3753 if (!async_send)
3755 memset( &s->ovr, 0, sizeof(s->ovr) );
3756 if ((ret = send_frame( socket, SOCKET_OPCODE_PONG, 0, NULL, 0, TRUE, &s->ovr )) == WSA_IO_PENDING)
3758 async_send = TRUE;
3759 complete_async = TRUE;
3763 if (async_send)
3765 s->complete_async = complete_async;
3766 if ((ret = queue_task( &socket->send_q, task_socket_send_pong, &s->task_hdr, &socket->hdr )))
3768 InterlockedDecrement( &socket->hdr.pending_sends );
3769 free( s );
3772 else
3774 InterlockedDecrement( &socket->hdr.pending_sends );
3775 free( s );
3777 ReleaseSRWLockExclusive( &socket->send_lock );
3778 return ret;
3780 return send_frame( socket, SOCKET_OPCODE_PONG, 0, NULL, 0, TRUE, NULL );
3783 static DWORD socket_drain( struct socket *socket )
3785 DWORD ret, count;
3787 while (socket->read_size)
3789 char buf[1024];
3790 if ((ret = receive_bytes( socket, buf, min(socket->read_size, sizeof(buf)), &count, TRUE ))) return ret;
3791 socket->read_size -= count;
3793 return ERROR_SUCCESS;
3796 static DWORD receive_close_status( struct socket *socket, DWORD len )
3798 DWORD reason_len, ret;
3800 socket->close_frame_received = TRUE;
3801 if ((len && (len < sizeof(socket->status) || len > sizeof(socket->status) + sizeof(socket->reason))))
3802 return (socket->close_frame_receive_err = ERROR_WINHTTP_INVALID_SERVER_RESPONSE);
3804 if (!len) return (socket->close_frame_receive_err = ERROR_SUCCESS);
3806 reason_len = len - sizeof(socket->status);
3807 if ((ret = receive_bytes( socket, (char *)&socket->status, sizeof(socket->status), &len, TRUE )))
3808 return (socket->close_frame_receive_err = ret);
3809 socket->status = RtlUshortByteSwap( socket->status );
3810 return (socket->close_frame_receive_err
3811 = receive_bytes( socket, socket->reason, reason_len, &socket->reason_len, TRUE ));
3814 static DWORD handle_control_frame( struct socket *socket )
3816 DWORD ret;
3818 TRACE( "opcode %u.\n", socket->opcode );
3820 switch (socket->opcode)
3822 case SOCKET_OPCODE_PING:
3823 return socket_send_pong( socket );
3825 case SOCKET_OPCODE_PONG:
3826 return socket_drain( socket );
3828 case SOCKET_OPCODE_CLOSE:
3829 if (socket->state < SOCKET_STATE_SHUTDOWN)
3830 WARN( "SOCKET_OPCODE_CLOSE received, socket->state %u.\n", socket->state );
3831 if (socket->close_frame_received)
3833 FIXME( "Close frame already received.\n" );
3834 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
3837 ret = receive_close_status( socket, socket->read_size );
3838 socket->read_size = 0;
3839 return ret;
3841 default:
3842 ERR("unhandled control opcode %02x\n", socket->opcode);
3843 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
3846 return ERROR_SUCCESS;
3849 static WINHTTP_WEB_SOCKET_BUFFER_TYPE map_opcode( struct socket *socket, enum socket_opcode opcode, BOOL fragment )
3851 enum fragment_type frag_type = socket->receiving_fragment_type;
3853 switch (opcode)
3855 case SOCKET_OPCODE_TEXT:
3856 if (frag_type && frag_type != SOCKET_FRAGMENT_UTF8)
3857 FIXME( "Received SOCKET_OPCODE_TEXT with prev fragment %u.\n", frag_type );
3858 if (fragment)
3860 socket->receiving_fragment_type = SOCKET_FRAGMENT_UTF8;
3861 return WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE;
3863 socket->receiving_fragment_type = SOCKET_FRAGMENT_NONE;
3864 return WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE;
3866 case SOCKET_OPCODE_BINARY:
3867 if (frag_type && frag_type != SOCKET_FRAGMENT_BINARY)
3868 FIXME( "Received SOCKET_OPCODE_BINARY with prev fragment %u.\n", frag_type );
3869 if (fragment)
3871 socket->receiving_fragment_type = SOCKET_FRAGMENT_BINARY;
3872 return WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE;
3874 socket->receiving_fragment_type = SOCKET_FRAGMENT_NONE;
3875 return WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE;
3877 case SOCKET_OPCODE_CONTINUE:
3878 if (!frag_type)
3880 FIXME( "Received SOCKET_OPCODE_CONTINUE without starting fragment.\n" );
3881 return ~0u;
3883 if (fragment)
3885 return frag_type == SOCKET_FRAGMENT_BINARY ? WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE
3886 : WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE;
3888 socket->receiving_fragment_type = SOCKET_FRAGMENT_NONE;
3889 return frag_type == SOCKET_FRAGMENT_BINARY ? WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE
3890 : WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE;
3892 case SOCKET_OPCODE_CLOSE:
3893 return WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE;
3895 default:
3896 FIXME("opcode %02x not handled\n", opcode);
3897 return ~0u;
3901 static DWORD socket_receive( struct socket *socket, void *buf, DWORD len, DWORD *ret_len,
3902 WINHTTP_WEB_SOCKET_BUFFER_TYPE *ret_type )
3904 BOOL final = socket->last_receive_final;
3905 DWORD count, ret = ERROR_SUCCESS;
3907 if (!socket->read_size)
3909 for (;;)
3911 if (!(ret = receive_frame( socket, &socket->read_size, &socket->opcode, &final )))
3913 if (!(socket->opcode & CONTROL_BIT) || (ret = handle_control_frame( socket ))
3914 || socket->opcode == SOCKET_OPCODE_CLOSE) break;
3916 else if (ret == WSAETIMEDOUT) ret = socket_send_pong( socket );
3917 if (ret) break;
3920 if (!ret)
3922 socket->last_receive_final = final;
3923 ret = receive_bytes( socket, buf, min(len, socket->read_size), &count, FALSE );
3925 if (!ret)
3927 if (count < socket->read_size)
3928 WARN("Short read.\n");
3930 socket->read_size -= count;
3931 *ret_len = count;
3932 *ret_type = map_opcode( socket, socket->opcode, !final || socket->read_size != 0 );
3933 TRACE( "len %lu, *ret_len %lu, *ret_type %u.\n", len, *ret_len, *ret_type );
3934 if (*ret_type == ~0u)
3936 FIXME( "Unexpected opcode %u.\n", socket->opcode );
3937 socket->read_size = 0;
3938 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
3941 return ret;
3944 static void socket_receive_complete( struct socket *socket, DWORD ret, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, DWORD len )
3946 if (!ret)
3948 WINHTTP_WEB_SOCKET_STATUS status;
3949 status.dwBytesTransferred = len;
3950 status.eBufferType = type;
3951 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, &status, sizeof(status) );
3953 else
3955 WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
3956 result.AsyncResult.dwResult = 0;
3957 result.AsyncResult.dwError = ret;
3958 result.Operation = WINHTTP_WEB_SOCKET_RECEIVE_OPERATION;
3959 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
3963 static void task_socket_receive( void *ctx, BOOL abort )
3965 struct socket_receive *r = ctx;
3966 struct socket *socket = (struct socket *)r->task_hdr.obj;
3967 DWORD ret, count;
3968 WINHTTP_WEB_SOCKET_BUFFER_TYPE type;
3970 if (abort)
3972 socket_receive_complete( socket, ERROR_WINHTTP_OPERATION_CANCELLED, 0, 0 );
3973 return;
3976 TRACE("running %p\n", ctx);
3977 ret = socket_receive( socket, r->buf, r->len, &count, &type );
3978 receive_io_complete( socket );
3979 if (task_needs_completion( &r->task_hdr ))
3980 socket_receive_complete( socket, ret, type, count );
3983 DWORD WINAPI WinHttpWebSocketReceive( HINTERNET hsocket, void *buf, DWORD len, DWORD *ret_len,
3984 WINHTTP_WEB_SOCKET_BUFFER_TYPE *ret_type )
3986 struct socket *socket;
3987 DWORD ret;
3989 TRACE( "%p, %p, %lu, %p, %p\n", hsocket, buf, len, ret_len, ret_type );
3991 if (!buf || !len) return ERROR_INVALID_PARAMETER;
3993 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
3994 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
3996 release_object( &socket->hdr );
3997 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
3999 if (!socket_can_receive( socket ))
4001 release_object( &socket->hdr );
4002 return ERROR_INVALID_OPERATION;
4005 if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
4007 struct socket_receive *r;
4009 if (InterlockedIncrement( &socket->hdr.pending_receives ) > 1)
4011 InterlockedDecrement( &socket->hdr.pending_receives );
4012 WARN( "Attempt to queue receive while another is pending.\n" );
4013 release_object( &socket->hdr );
4014 return ERROR_INVALID_OPERATION;
4017 if (!(r = malloc( sizeof(*r) )))
4019 InterlockedDecrement( &socket->hdr.pending_receives );
4020 release_object( &socket->hdr );
4021 return ERROR_OUTOFMEMORY;
4023 r->buf = buf;
4024 r->len = len;
4026 if ((ret = queue_task( &socket->recv_q, task_socket_receive, &r->task_hdr, &socket->hdr )))
4028 InterlockedDecrement( &socket->hdr.pending_receives );
4029 free( r );
4032 else ret = socket_receive( socket, buf, len, ret_len, ret_type );
4034 release_object( &socket->hdr );
4035 return ret;
4038 static void socket_shutdown_complete( struct socket *socket, DWORD ret )
4040 if (!ret) send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE, NULL, 0 );
4041 else
4043 WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
4044 result.AsyncResult.dwResult = API_WRITE_DATA;
4045 result.AsyncResult.dwError = ret;
4046 result.Operation = WINHTTP_WEB_SOCKET_SHUTDOWN_OPERATION;
4047 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
4051 static void task_socket_shutdown( void *ctx, BOOL abort )
4053 struct socket_shutdown *s = ctx;
4054 struct socket *socket = (struct socket *)s->task_hdr.obj;
4055 DWORD ret;
4057 if (abort) return;
4059 TRACE("running %p\n", ctx);
4061 if (s->complete_async) ret = complete_send_frame( socket, &s->ovr, s->reason );
4062 else ret = send_frame( socket, SOCKET_OPCODE_CLOSE, s->status, s->reason, s->len, TRUE, NULL );
4064 send_io_complete( &socket->hdr );
4065 if (s->send_callback) socket_shutdown_complete( socket, ret );
4068 static DWORD send_socket_shutdown( struct socket *socket, USHORT status, const void *reason, DWORD len,
4069 BOOL send_callback)
4071 DWORD ret;
4073 if (socket->state < SOCKET_STATE_SHUTDOWN) socket->state = SOCKET_STATE_SHUTDOWN;
4075 if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
4077 BOOL async_send, complete_async = FALSE;
4078 struct socket_shutdown *s;
4080 if (!(s = malloc( sizeof(*s) ))) return FALSE;
4082 AcquireSRWLockExclusive( &socket->send_lock );
4083 async_send = InterlockedIncrement( &socket->hdr.pending_sends ) > 1 || socket->hdr.recursion_count >= 3;
4084 if (!async_send)
4086 memset( &s->ovr, 0, sizeof(s->ovr) );
4087 if ((ret = send_frame( socket, SOCKET_OPCODE_CLOSE, status, reason, len, TRUE, &s->ovr )) == WSA_IO_PENDING)
4089 async_send = TRUE;
4090 complete_async = TRUE;
4094 if (async_send)
4096 s->complete_async = complete_async;
4097 s->status = status;
4098 memcpy( s->reason, reason, len );
4099 s->len = len;
4100 s->send_callback = send_callback;
4102 if ((ret = queue_task( &socket->send_q, task_socket_shutdown, &s->task_hdr, &socket->hdr )))
4104 InterlockedDecrement( &socket->hdr.pending_sends );
4105 free( s );
4108 else InterlockedDecrement( &socket->hdr.pending_sends );
4109 ReleaseSRWLockExclusive( &socket->send_lock );
4110 if (!async_send)
4112 free( s );
4113 if (send_callback)
4115 socket_shutdown_complete( socket, ret );
4116 ret = ERROR_SUCCESS;
4120 else ret = send_frame( socket, SOCKET_OPCODE_CLOSE, status, reason, len, TRUE, NULL );
4122 return ret;
4125 DWORD WINAPI WinHttpWebSocketShutdown( HINTERNET hsocket, USHORT status, void *reason, DWORD len )
4127 struct socket *socket;
4128 DWORD ret;
4130 TRACE( "%p, %u, %p, %lu\n", hsocket, status, reason, len );
4132 if ((len && !reason) || len > sizeof(socket->reason)) return ERROR_INVALID_PARAMETER;
4134 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
4135 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
4137 release_object( &socket->hdr );
4138 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
4140 if (socket->state >= SOCKET_STATE_SHUTDOWN)
4142 release_object( &socket->hdr );
4143 return ERROR_INVALID_OPERATION;
4146 ret = send_socket_shutdown( socket, status, reason, len, TRUE );
4147 release_object( &socket->hdr );
4148 return ret;
4151 static DWORD socket_close( struct socket *socket )
4153 BOOL final = FALSE;
4154 DWORD ret, count;
4156 if (socket->close_frame_received) return socket->close_frame_receive_err;
4158 if ((ret = socket_drain( socket ))) return ret;
4160 while (1)
4162 if ((ret = receive_frame( socket, &count, &socket->opcode, &final ))) return ret;
4163 if (socket->opcode == SOCKET_OPCODE_CLOSE) break;
4165 socket->read_size = count;
4166 if ((ret = socket_drain( socket ))) return ret;
4168 if (!final)
4169 FIXME( "Received close opcode without FIN bit.\n" );
4171 return receive_close_status( socket, count );
4174 static void socket_close_complete( struct socket *socket, DWORD ret )
4176 if (!ret) send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE, NULL, 0 );
4177 else
4179 WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
4180 result.AsyncResult.dwResult = API_READ_DATA; /* FIXME */
4181 result.AsyncResult.dwError = ret;
4182 result.Operation = WINHTTP_WEB_SOCKET_CLOSE_OPERATION;
4183 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
4187 static void task_socket_close( void *ctx, BOOL abort )
4189 struct socket_shutdown *s = ctx;
4190 struct socket *socket = (struct socket *)s->task_hdr.obj;
4191 DWORD ret;
4193 if (abort)
4195 socket_close_complete( socket, ERROR_WINHTTP_OPERATION_CANCELLED );
4196 return;
4199 TRACE("running %p\n", ctx);
4201 ret = socket_close( socket );
4202 receive_io_complete( socket );
4203 if (task_needs_completion( &s->task_hdr ))
4204 socket_close_complete( socket, ret );
4207 DWORD WINAPI WinHttpWebSocketClose( HINTERNET hsocket, USHORT status, void *reason, DWORD len )
4209 enum socket_state prev_state;
4210 LONG pending_receives = 0;
4211 struct socket *socket;
4212 DWORD ret;
4214 TRACE( "%p, %u, %p, %lu\n", hsocket, status, reason, len );
4216 if ((len && !reason) || len > sizeof(socket->reason)) return ERROR_INVALID_PARAMETER;
4218 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
4219 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
4221 release_object( &socket->hdr );
4222 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
4224 if (socket->state >= SOCKET_STATE_CLOSED)
4226 release_object( &socket->hdr );
4227 return ERROR_INVALID_OPERATION;
4230 prev_state = socket->state;
4231 socket->state = SOCKET_STATE_CLOSED;
4233 if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
4235 pending_receives = InterlockedIncrement( &socket->hdr.pending_receives );
4236 cancel_queue( &socket->recv_q );
4239 if (prev_state < SOCKET_STATE_SHUTDOWN && (ret = send_socket_shutdown( socket, status, reason, len, FALSE )))
4240 goto done;
4242 if (pending_receives == 1 && socket->close_frame_received)
4244 if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
4245 socket_close_complete( socket, socket->close_frame_receive_err );
4246 goto done;
4249 if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
4251 struct socket_shutdown *s;
4253 if (!(s = calloc( 1, sizeof(*s) )))
4255 ret = ERROR_OUTOFMEMORY;
4256 goto done;
4258 if ((ret = queue_task( &socket->recv_q, task_socket_close, &s->task_hdr, &socket->hdr )))
4260 InterlockedDecrement( &socket->hdr.pending_receives );
4261 free( s );
4264 else ret = socket_close( socket );
4266 done:
4267 release_object( &socket->hdr );
4268 return ret;
4271 DWORD WINAPI WinHttpWebSocketQueryCloseStatus( HINTERNET hsocket, USHORT *status, void *reason, DWORD len,
4272 DWORD *ret_len )
4274 struct socket *socket;
4275 DWORD ret;
4277 TRACE( "%p, %p, %p, %lu, %p\n", hsocket, status, reason, len, ret_len );
4279 if (!status || (len && !reason) || !ret_len) return ERROR_INVALID_PARAMETER;
4281 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
4282 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
4284 release_object( &socket->hdr );
4285 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
4288 if (!socket->close_frame_received || socket->close_frame_receive_err)
4290 ret = socket->close_frame_received ? socket->close_frame_receive_err : ERROR_INVALID_OPERATION;
4291 release_object( &socket->hdr );
4292 return ret;
4294 *status = socket->status;
4295 *ret_len = socket->reason_len;
4296 if (socket->reason_len > len) ret = ERROR_INSUFFICIENT_BUFFER;
4297 else
4299 memcpy( reason, socket->reason, socket->reason_len );
4300 ret = ERROR_SUCCESS;
4303 release_object( &socket->hdr );
4304 return ret;
4307 enum request_state
4309 REQUEST_STATE_INITIALIZED,
4310 REQUEST_STATE_CANCELLED,
4311 REQUEST_STATE_OPEN,
4312 REQUEST_STATE_SENT,
4313 REQUEST_STATE_RESPONSE_RECEIVED
4316 struct winhttp_request
4318 IWinHttpRequest IWinHttpRequest_iface;
4319 LONG refs;
4320 CRITICAL_SECTION cs;
4321 enum request_state state;
4322 HINTERNET hsession;
4323 HINTERNET hconnect;
4324 HINTERNET hrequest;
4325 VARIANT data;
4326 WCHAR *verb;
4327 HANDLE done;
4328 HANDLE wait;
4329 HANDLE cancel;
4330 BOOL proc_running;
4331 char *buffer;
4332 DWORD offset;
4333 DWORD bytes_available;
4334 DWORD bytes_read;
4335 DWORD error;
4336 DWORD logon_policy;
4337 DWORD disable_feature;
4338 LONG resolve_timeout;
4339 LONG connect_timeout;
4340 LONG send_timeout;
4341 LONG receive_timeout;
4342 WINHTTP_PROXY_INFO proxy;
4343 BOOL async;
4344 UINT url_codepage;
4347 static inline struct winhttp_request *impl_from_IWinHttpRequest( IWinHttpRequest *iface )
4349 return CONTAINING_RECORD( iface, struct winhttp_request, IWinHttpRequest_iface );
4352 static ULONG WINAPI winhttp_request_AddRef(
4353 IWinHttpRequest *iface )
4355 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4356 return InterlockedIncrement( &request->refs );
4359 /* critical section must be held */
4360 static void cancel_request( struct winhttp_request *request )
4362 if (request->state <= REQUEST_STATE_CANCELLED) return;
4364 if (request->proc_running)
4366 SetEvent( request->cancel );
4367 LeaveCriticalSection( &request->cs );
4369 WaitForSingleObject( request->done, INFINITE );
4371 EnterCriticalSection( &request->cs );
4373 request->state = REQUEST_STATE_CANCELLED;
4376 /* critical section must be held */
4377 static void free_request( struct winhttp_request *request )
4379 if (request->state < REQUEST_STATE_INITIALIZED) return;
4380 WinHttpCloseHandle( request->hrequest );
4381 WinHttpCloseHandle( request->hconnect );
4382 WinHttpCloseHandle( request->hsession );
4383 CloseHandle( request->done );
4384 CloseHandle( request->wait );
4385 CloseHandle( request->cancel );
4386 free( request->proxy.lpszProxy );
4387 free( request->proxy.lpszProxyBypass );
4388 free( request->buffer );
4389 free( request->verb );
4390 VariantClear( &request->data );
4393 static ULONG WINAPI winhttp_request_Release(
4394 IWinHttpRequest *iface )
4396 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4397 LONG refs = InterlockedDecrement( &request->refs );
4398 if (!refs)
4400 TRACE("destroying %p\n", request);
4402 EnterCriticalSection( &request->cs );
4403 cancel_request( request );
4404 free_request( request );
4405 LeaveCriticalSection( &request->cs );
4406 request->cs.DebugInfo->Spare[0] = 0;
4407 DeleteCriticalSection( &request->cs );
4408 free( request );
4410 return refs;
4413 static HRESULT WINAPI winhttp_request_QueryInterface(
4414 IWinHttpRequest *iface,
4415 REFIID riid,
4416 void **obj )
4418 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4420 TRACE("%p, %s, %p\n", request, debugstr_guid(riid), obj );
4422 if (IsEqualGUID( riid, &IID_IWinHttpRequest ) ||
4423 IsEqualGUID( riid, &IID_IDispatch ) ||
4424 IsEqualGUID( riid, &IID_IUnknown ))
4426 *obj = iface;
4428 else
4430 FIXME("interface %s not implemented\n", debugstr_guid(riid));
4431 return E_NOINTERFACE;
4433 IWinHttpRequest_AddRef( iface );
4434 return S_OK;
4437 static HRESULT WINAPI winhttp_request_GetTypeInfoCount(
4438 IWinHttpRequest *iface,
4439 UINT *count )
4441 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4443 TRACE("%p, %p\n", request, count);
4444 *count = 1;
4445 return S_OK;
4448 enum type_id
4450 IWinHttpRequest_tid,
4451 last_tid
4454 static ITypeLib *winhttp_typelib;
4455 static ITypeInfo *winhttp_typeinfo[last_tid];
4457 static REFIID winhttp_tid_id[] =
4459 &IID_IWinHttpRequest
4462 static HRESULT get_typeinfo( enum type_id tid, ITypeInfo **ret )
4464 HRESULT hr;
4466 if (!winhttp_typelib)
4468 ITypeLib *typelib;
4470 hr = LoadRegTypeLib( &LIBID_WinHttp, 5, 1, LOCALE_SYSTEM_DEFAULT, &typelib );
4471 if (FAILED(hr))
4473 ERR( "LoadRegTypeLib failed: %#lx\n", hr );
4474 return hr;
4476 if (InterlockedCompareExchangePointer( (void **)&winhttp_typelib, typelib, NULL ))
4477 ITypeLib_Release( typelib );
4479 if (!winhttp_typeinfo[tid])
4481 ITypeInfo *typeinfo;
4483 hr = ITypeLib_GetTypeInfoOfGuid( winhttp_typelib, winhttp_tid_id[tid], &typeinfo );
4484 if (FAILED(hr))
4486 ERR( "GetTypeInfoOfGuid(%s) failed: %#lx\n", debugstr_guid(winhttp_tid_id[tid]), hr );
4487 return hr;
4489 if (InterlockedCompareExchangePointer( (void **)(winhttp_typeinfo + tid), typeinfo, NULL ))
4490 ITypeInfo_Release( typeinfo );
4492 *ret = winhttp_typeinfo[tid];
4493 ITypeInfo_AddRef(winhttp_typeinfo[tid]);
4494 return S_OK;
4497 void release_typelib(void)
4499 unsigned i;
4501 for (i = 0; i < ARRAY_SIZE(winhttp_typeinfo); i++)
4502 if (winhttp_typeinfo[i])
4503 ITypeInfo_Release(winhttp_typeinfo[i]);
4505 if (winhttp_typelib)
4506 ITypeLib_Release(winhttp_typelib);
4509 static HRESULT WINAPI winhttp_request_GetTypeInfo(
4510 IWinHttpRequest *iface,
4511 UINT index,
4512 LCID lcid,
4513 ITypeInfo **info )
4515 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4516 TRACE( "%p, %u, %lu, %p\n", request, index, lcid, info );
4518 return get_typeinfo( IWinHttpRequest_tid, info );
4521 static HRESULT WINAPI winhttp_request_GetIDsOfNames(
4522 IWinHttpRequest *iface,
4523 REFIID riid,
4524 LPOLESTR *names,
4525 UINT count,
4526 LCID lcid,
4527 DISPID *dispid )
4529 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4530 ITypeInfo *typeinfo;
4531 HRESULT hr;
4533 TRACE( "%p, %s, %p, %u, %lu, %p\n", request, debugstr_guid(riid), names, count, lcid, dispid );
4535 if (!names || !count || !dispid) return E_INVALIDARG;
4537 hr = get_typeinfo( IWinHttpRequest_tid, &typeinfo );
4538 if (SUCCEEDED(hr))
4540 hr = ITypeInfo_GetIDsOfNames( typeinfo, names, count, dispid );
4541 ITypeInfo_Release( typeinfo );
4543 return hr;
4546 static HRESULT WINAPI winhttp_request_Invoke(
4547 IWinHttpRequest *iface,
4548 DISPID member,
4549 REFIID riid,
4550 LCID lcid,
4551 WORD flags,
4552 DISPPARAMS *params,
4553 VARIANT *result,
4554 EXCEPINFO *excep_info,
4555 UINT *arg_err )
4557 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4558 ITypeInfo *typeinfo;
4559 HRESULT hr;
4561 TRACE( "%p, %ld, %s, %lu, %d, %p, %p, %p, %p\n", request, member, debugstr_guid(riid),
4562 lcid, flags, params, result, excep_info, arg_err );
4564 if (!IsEqualIID( riid, &IID_NULL )) return DISP_E_UNKNOWNINTERFACE;
4566 if (member == DISPID_HTTPREQUEST_OPTION)
4568 VARIANT ret_value, option;
4569 UINT err_pos;
4571 if (!result) result = &ret_value;
4572 if (!arg_err) arg_err = &err_pos;
4574 VariantInit( &option );
4575 VariantInit( result );
4577 if (!flags) return S_OK;
4579 if (flags == DISPATCH_PROPERTYPUT)
4581 hr = DispGetParam( params, 0, VT_I4, &option, arg_err );
4582 if (FAILED(hr)) return hr;
4584 hr = IWinHttpRequest_put_Option( &request->IWinHttpRequest_iface, V_I4( &option ), params->rgvarg[0] );
4585 if (FAILED(hr))
4586 WARN( "put_Option(%ld) failed: %#lx\n", V_I4( &option ), hr );
4587 return hr;
4589 else if (flags & (DISPATCH_PROPERTYGET | DISPATCH_METHOD))
4591 hr = DispGetParam( params, 0, VT_I4, &option, arg_err );
4592 if (FAILED(hr)) return hr;
4594 hr = IWinHttpRequest_get_Option( &request->IWinHttpRequest_iface, V_I4( &option ), result );
4595 if (FAILED(hr))
4596 WARN( "get_Option(%ld) failed: %#lx\n", V_I4( &option ), hr );
4597 return hr;
4600 FIXME("unsupported flags %x\n", flags);
4601 return E_NOTIMPL;
4604 /* fallback to standard implementation */
4606 hr = get_typeinfo( IWinHttpRequest_tid, &typeinfo );
4607 if (SUCCEEDED(hr))
4609 hr = ITypeInfo_Invoke( typeinfo, &request->IWinHttpRequest_iface, member, flags,
4610 params, result, excep_info, arg_err );
4611 ITypeInfo_Release( typeinfo );
4613 return hr;
4616 static HRESULT WINAPI winhttp_request_SetProxy(
4617 IWinHttpRequest *iface,
4618 HTTPREQUEST_PROXY_SETTING proxy_setting,
4619 VARIANT proxy_server,
4620 VARIANT bypass_list )
4622 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4623 DWORD err = ERROR_SUCCESS;
4625 TRACE( "%p, %lu, %s, %s\n", request, proxy_setting, debugstr_variant(&proxy_server),
4626 debugstr_variant(&bypass_list) );
4628 EnterCriticalSection( &request->cs );
4629 switch (proxy_setting)
4631 case HTTPREQUEST_PROXYSETTING_DEFAULT:
4632 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
4633 free( request->proxy.lpszProxy );
4634 free( request->proxy.lpszProxyBypass );
4635 request->proxy.lpszProxy = NULL;
4636 request->proxy.lpszProxyBypass = NULL;
4637 break;
4639 case HTTPREQUEST_PROXYSETTING_DIRECT:
4640 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NO_PROXY;
4641 free( request->proxy.lpszProxy );
4642 free( request->proxy.lpszProxyBypass );
4643 request->proxy.lpszProxy = NULL;
4644 request->proxy.lpszProxyBypass = NULL;
4645 break;
4647 case HTTPREQUEST_PROXYSETTING_PROXY:
4648 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
4649 if (V_VT( &proxy_server ) == VT_BSTR)
4651 free( request->proxy.lpszProxy );
4652 request->proxy.lpszProxy = strdupW( V_BSTR( &proxy_server ) );
4654 if (V_VT( &bypass_list ) == VT_BSTR)
4656 free( request->proxy.lpszProxyBypass );
4657 request->proxy.lpszProxyBypass = strdupW( V_BSTR( &bypass_list ) );
4659 break;
4661 default:
4662 err = ERROR_INVALID_PARAMETER;
4663 break;
4665 LeaveCriticalSection( &request->cs );
4666 return HRESULT_FROM_WIN32( err );
4669 static HRESULT WINAPI winhttp_request_SetCredentials(
4670 IWinHttpRequest *iface,
4671 BSTR username,
4672 BSTR password,
4673 HTTPREQUEST_SETCREDENTIALS_FLAGS flags )
4675 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4676 DWORD target, scheme = WINHTTP_AUTH_SCHEME_BASIC; /* FIXME: query supported schemes */
4677 DWORD err = ERROR_SUCCESS;
4679 TRACE( "%p, %s, %p, %#lx\n", request, debugstr_w(username), password, flags );
4681 EnterCriticalSection( &request->cs );
4682 if (request->state < REQUEST_STATE_OPEN)
4684 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN;
4685 goto done;
4687 switch (flags)
4689 case HTTPREQUEST_SETCREDENTIALS_FOR_SERVER:
4690 target = WINHTTP_AUTH_TARGET_SERVER;
4691 break;
4692 case HTTPREQUEST_SETCREDENTIALS_FOR_PROXY:
4693 target = WINHTTP_AUTH_TARGET_PROXY;
4694 break;
4695 default:
4696 err = ERROR_INVALID_PARAMETER;
4697 goto done;
4699 if (!WinHttpSetCredentials( request->hrequest, target, scheme, username, password, NULL ))
4701 err = GetLastError();
4703 done:
4704 LeaveCriticalSection( &request->cs );
4705 return HRESULT_FROM_WIN32( err );
4708 static void initialize_request( struct winhttp_request *request )
4710 request->wait = CreateEventW( NULL, FALSE, FALSE, NULL );
4711 request->cancel = CreateEventW( NULL, FALSE, FALSE, NULL );
4712 request->done = CreateEventW( NULL, FALSE, FALSE, NULL );
4713 request->connect_timeout = 60000;
4714 request->send_timeout = 30000;
4715 request->receive_timeout = 30000;
4716 request->url_codepage = CP_UTF8;
4717 VariantInit( &request->data );
4718 request->state = REQUEST_STATE_INITIALIZED;
4721 static void reset_request( struct winhttp_request *request )
4723 cancel_request( request );
4724 WinHttpCloseHandle( request->hrequest );
4725 request->hrequest = NULL;
4726 WinHttpCloseHandle( request->hconnect );
4727 request->hconnect = NULL;
4728 free( request->buffer );
4729 request->buffer = NULL;
4730 free( request->verb );
4731 request->verb = NULL;
4732 request->offset = 0;
4733 request->bytes_available = 0;
4734 request->bytes_read = 0;
4735 request->error = ERROR_SUCCESS;
4736 request->logon_policy = 0;
4737 request->disable_feature = 0;
4738 request->async = FALSE;
4739 request->connect_timeout = 60000;
4740 request->send_timeout = 30000;
4741 request->receive_timeout = 30000;
4742 request->url_codepage = CP_UTF8;
4743 free( request->proxy.lpszProxy );
4744 request->proxy.lpszProxy = NULL;
4745 free( request->proxy.lpszProxyBypass );
4746 request->proxy.lpszProxyBypass = NULL;
4747 VariantClear( &request->data );
4748 request->state = REQUEST_STATE_INITIALIZED;
4751 static HRESULT WINAPI winhttp_request_Open(
4752 IWinHttpRequest *iface,
4753 BSTR method,
4754 BSTR url,
4755 VARIANT async )
4757 static const WCHAR httpsW[] = {'h','t','t','p','s'};
4758 static const WCHAR *acceptW[] = {L"*/*", NULL};
4759 static const WCHAR user_agentW[] = L"Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)";
4760 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4761 URL_COMPONENTS uc;
4762 WCHAR *hostname, *path = NULL, *verb = NULL;
4763 DWORD err = ERROR_OUTOFMEMORY, len, flags = 0;
4765 TRACE("%p, %s, %s, %s\n", request, debugstr_w(method), debugstr_w(url),
4766 debugstr_variant(&async));
4768 if (!method || !url) return E_INVALIDARG;
4770 memset( &uc, 0, sizeof(uc) );
4771 uc.dwStructSize = sizeof(uc);
4772 uc.dwSchemeLength = ~0u;
4773 uc.dwHostNameLength = ~0u;
4774 uc.dwUrlPathLength = ~0u;
4775 uc.dwExtraInfoLength = ~0u;
4776 if (!WinHttpCrackUrl( url, 0, 0, &uc )) return HRESULT_FROM_WIN32( GetLastError() );
4778 EnterCriticalSection( &request->cs );
4779 reset_request( request );
4781 if (!(hostname = malloc( (uc.dwHostNameLength + 1) * sizeof(WCHAR) ))) goto error;
4782 memcpy( hostname, uc.lpszHostName, uc.dwHostNameLength * sizeof(WCHAR) );
4783 hostname[uc.dwHostNameLength] = 0;
4785 if (!(path = malloc( (uc.dwUrlPathLength + uc.dwExtraInfoLength + 1) * sizeof(WCHAR) ))) goto error;
4786 memcpy( path, uc.lpszUrlPath, (uc.dwUrlPathLength + uc.dwExtraInfoLength) * sizeof(WCHAR) );
4787 path[uc.dwUrlPathLength + uc.dwExtraInfoLength] = 0;
4789 if (!(verb = strdupW( method ))) goto error;
4790 if (SUCCEEDED( VariantChangeType( &async, &async, 0, VT_BOOL )) && V_BOOL( &async )) request->async = TRUE;
4791 else request->async = FALSE;
4793 if (!request->hsession)
4795 if (!(request->hsession = WinHttpOpen( user_agentW, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, NULL, NULL,
4796 WINHTTP_FLAG_ASYNC )))
4798 err = GetLastError();
4799 goto error;
4801 if (!(request->hconnect = WinHttpConnect( request->hsession, hostname, uc.nPort, 0 )))
4803 WinHttpCloseHandle( request->hsession );
4804 request->hsession = NULL;
4805 err = GetLastError();
4806 goto error;
4809 else if (!(request->hconnect = WinHttpConnect( request->hsession, hostname, uc.nPort, 0 )))
4811 err = GetLastError();
4812 goto error;
4815 len = ARRAY_SIZE( httpsW );
4816 if (uc.dwSchemeLength == len && !memcmp( uc.lpszScheme, httpsW, len * sizeof(WCHAR) ))
4818 flags |= WINHTTP_FLAG_SECURE;
4820 if (!(request->hrequest = WinHttpOpenRequest( request->hconnect, method, path, NULL, NULL, acceptW, flags )))
4822 err = GetLastError();
4823 goto error;
4825 WinHttpSetOption( request->hrequest, WINHTTP_OPTION_CONTEXT_VALUE, &request, sizeof(request) );
4827 request->state = REQUEST_STATE_OPEN;
4828 request->verb = verb;
4829 free( hostname );
4830 free( path );
4831 LeaveCriticalSection( &request->cs );
4832 return S_OK;
4834 error:
4835 WinHttpCloseHandle( request->hconnect );
4836 request->hconnect = NULL;
4837 free( hostname );
4838 free( path );
4839 free( verb );
4840 LeaveCriticalSection( &request->cs );
4841 return HRESULT_FROM_WIN32( err );
4844 static HRESULT WINAPI winhttp_request_SetRequestHeader(
4845 IWinHttpRequest *iface,
4846 BSTR header,
4847 BSTR value )
4849 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4850 DWORD len, err = ERROR_SUCCESS;
4851 WCHAR *str;
4853 TRACE("%p, %s, %s\n", request, debugstr_w(header), debugstr_w(value));
4855 if (!header) return E_INVALIDARG;
4857 EnterCriticalSection( &request->cs );
4858 if (request->state < REQUEST_STATE_OPEN)
4860 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN;
4861 goto done;
4863 if (request->state >= REQUEST_STATE_SENT)
4865 err = ERROR_WINHTTP_CANNOT_CALL_AFTER_SEND;
4866 goto done;
4868 len = lstrlenW( header ) + 4;
4869 if (value) len += lstrlenW( value );
4870 if (!(str = malloc( (len + 1) * sizeof(WCHAR) )))
4872 err = ERROR_OUTOFMEMORY;
4873 goto done;
4875 swprintf( str, len + 1, L"%s: %s\r\n", header, value ? value : L"" );
4876 if (!WinHttpAddRequestHeaders( request->hrequest, str, len,
4877 WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE ))
4879 err = GetLastError();
4881 free( str );
4883 done:
4884 LeaveCriticalSection( &request->cs );
4885 return HRESULT_FROM_WIN32( err );
4888 static HRESULT WINAPI winhttp_request_GetResponseHeader(
4889 IWinHttpRequest *iface,
4890 BSTR header,
4891 BSTR *value )
4893 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4894 DWORD size, err = ERROR_SUCCESS;
4896 TRACE("%p, %p\n", request, header);
4898 EnterCriticalSection( &request->cs );
4899 if (request->state < REQUEST_STATE_SENT)
4901 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
4902 goto done;
4904 if (!header || !value)
4906 err = ERROR_INVALID_PARAMETER;
4907 goto done;
4909 size = 0;
4910 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CUSTOM, header, NULL, &size, NULL ))
4912 err = GetLastError();
4913 if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
4915 if (!(*value = SysAllocStringLen( NULL, size / sizeof(WCHAR) )))
4917 err = ERROR_OUTOFMEMORY;
4918 goto done;
4920 err = ERROR_SUCCESS;
4921 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CUSTOM, header, *value, &size, NULL ))
4923 err = GetLastError();
4924 SysFreeString( *value );
4926 done:
4927 LeaveCriticalSection( &request->cs );
4928 return HRESULT_FROM_WIN32( err );
4931 static HRESULT WINAPI winhttp_request_GetAllResponseHeaders(
4932 IWinHttpRequest *iface,
4933 BSTR *headers )
4935 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4936 DWORD size, err = ERROR_SUCCESS;
4938 TRACE("%p, %p\n", request, headers);
4940 if (!headers) return E_INVALIDARG;
4942 EnterCriticalSection( &request->cs );
4943 if (request->state < REQUEST_STATE_SENT)
4945 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
4946 goto done;
4948 size = 0;
4949 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, NULL, &size, NULL ))
4951 err = GetLastError();
4952 if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
4954 if (!(*headers = SysAllocStringLen( NULL, size / sizeof(WCHAR) )))
4956 err = ERROR_OUTOFMEMORY;
4957 goto done;
4959 err = ERROR_SUCCESS;
4960 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, *headers, &size, NULL ))
4962 err = GetLastError();
4963 SysFreeString( *headers );
4965 done:
4966 LeaveCriticalSection( &request->cs );
4967 return HRESULT_FROM_WIN32( err );
4970 static void CALLBACK wait_status_callback( HINTERNET handle, DWORD_PTR context, DWORD status, LPVOID buffer, DWORD size )
4972 struct winhttp_request *request = (struct winhttp_request *)context;
4974 switch (status)
4976 case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
4977 request->bytes_available = *(DWORD *)buffer;
4978 request->error = ERROR_SUCCESS;
4979 break;
4980 case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
4981 request->bytes_read = size;
4982 request->error = ERROR_SUCCESS;
4983 break;
4984 case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
4986 WINHTTP_ASYNC_RESULT *result = (WINHTTP_ASYNC_RESULT *)buffer;
4987 request->error = result->dwError;
4988 break;
4990 default:
4991 request->error = ERROR_SUCCESS;
4992 break;
4994 SetEvent( request->wait );
4997 static void wait_set_status_callback( struct winhttp_request *request, DWORD status )
4999 status |= WINHTTP_CALLBACK_STATUS_REQUEST_ERROR;
5000 WinHttpSetStatusCallback( request->hrequest, wait_status_callback, status, 0 );
5003 static DWORD wait_for_completion( struct winhttp_request *request )
5005 HANDLE handles[2] = { request->wait, request->cancel };
5006 DWORD ret;
5008 switch (WaitForMultipleObjects( 2, handles, FALSE, INFINITE ))
5010 case WAIT_OBJECT_0:
5011 ret = request->error;
5012 break;
5013 case WAIT_OBJECT_0 + 1:
5014 ret = request->error = ERROR_CANCELLED;
5015 SetEvent( request->done );
5016 break;
5017 default:
5018 ret = request->error = GetLastError();
5019 break;
5021 return ret;
5024 static HRESULT request_receive( struct winhttp_request *request )
5026 DWORD err, size, buflen = 4096;
5028 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE );
5029 if (!WinHttpReceiveResponse( request->hrequest, NULL ))
5031 return HRESULT_FROM_WIN32( GetLastError() );
5033 if ((err = wait_for_completion( request ))) return HRESULT_FROM_WIN32( err );
5034 if (!wcscmp( request->verb, L"HEAD" ))
5036 request->state = REQUEST_STATE_RESPONSE_RECEIVED;
5037 return S_OK;
5039 if (!(request->buffer = malloc( buflen ))) return E_OUTOFMEMORY;
5040 request->buffer[0] = 0;
5041 size = 0;
5044 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE );
5045 if (!WinHttpQueryDataAvailable( request->hrequest, &request->bytes_available ))
5047 err = GetLastError();
5048 goto error;
5050 if ((err = wait_for_completion( request ))) goto error;
5051 if (!request->bytes_available) break;
5052 size += request->bytes_available;
5053 if (buflen < size)
5055 char *tmp;
5056 while (buflen < size) buflen *= 2;
5057 if (!(tmp = realloc( request->buffer, buflen )))
5059 err = ERROR_OUTOFMEMORY;
5060 goto error;
5062 request->buffer = tmp;
5064 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_READ_COMPLETE );
5065 if (!WinHttpReadData( request->hrequest, request->buffer + request->offset,
5066 request->bytes_available, &request->bytes_read ))
5068 err = GetLastError();
5069 goto error;
5071 if ((err = wait_for_completion( request ))) goto error;
5072 request->offset += request->bytes_read;
5073 } while (request->bytes_read);
5075 request->state = REQUEST_STATE_RESPONSE_RECEIVED;
5076 return S_OK;
5078 error:
5079 free( request->buffer );
5080 request->buffer = NULL;
5081 return HRESULT_FROM_WIN32( err );
5084 static DWORD request_set_parameters( struct winhttp_request *request )
5086 if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_PROXY, &request->proxy,
5087 sizeof(request->proxy) )) return GetLastError();
5089 if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_AUTOLOGON_POLICY, &request->logon_policy,
5090 sizeof(request->logon_policy) )) return GetLastError();
5092 if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_DISABLE_FEATURE, &request->disable_feature,
5093 sizeof(request->disable_feature) )) return GetLastError();
5095 if (!WinHttpSetTimeouts( request->hrequest,
5096 request->resolve_timeout,
5097 request->connect_timeout,
5098 request->send_timeout,
5099 request->receive_timeout )) return GetLastError();
5100 return ERROR_SUCCESS;
5103 static void request_set_utf8_content_type( struct winhttp_request *request )
5105 WCHAR headerW[64];
5106 int len;
5108 len = swprintf( headerW, ARRAY_SIZE(headerW), L"%s: %s", L"Content-Type", L"text/plain" );
5109 WinHttpAddRequestHeaders( request->hrequest, headerW, len, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW );
5111 len = swprintf( headerW, ARRAY_SIZE(headerW), L"%s: %s", L"Content-Type", L"charset=utf-8" );
5112 WinHttpAddRequestHeaders( request->hrequest, headerW, len, WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON );
5115 static HRESULT request_send( struct winhttp_request *request )
5117 SAFEARRAY *sa = NULL;
5118 VARIANT data;
5119 char *ptr = NULL;
5120 LONG size = 0;
5121 HRESULT hr;
5122 DWORD err;
5124 if ((err = request_set_parameters( request ))) return HRESULT_FROM_WIN32( err );
5125 if (wcscmp( request->verb, L"GET" ))
5127 VariantInit( &data );
5128 if (V_VT( &request->data ) == VT_BSTR)
5130 UINT cp = CP_ACP;
5131 const WCHAR *str = V_BSTR( &request->data );
5132 int i, len = lstrlenW( str );
5134 for (i = 0; i < len; i++)
5136 if (str[i] > 127)
5138 cp = CP_UTF8;
5139 break;
5142 size = WideCharToMultiByte( cp, 0, str, len, NULL, 0, NULL, NULL );
5143 if (!(ptr = malloc( size ))) return E_OUTOFMEMORY;
5144 WideCharToMultiByte( cp, 0, str, len, ptr, size, NULL, NULL );
5145 if (cp == CP_UTF8) request_set_utf8_content_type( request );
5147 else if (VariantChangeType( &data, &request->data, 0, VT_ARRAY|VT_UI1 ) == S_OK)
5149 sa = V_ARRAY( &data );
5150 if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK) return hr;
5151 if ((hr = SafeArrayGetUBound( sa, 1, &size )) != S_OK)
5153 SafeArrayUnaccessData( sa );
5154 return hr;
5156 size++;
5159 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_REQUEST_SENT );
5160 if (!WinHttpSendRequest( request->hrequest, NULL, 0, ptr, size, size, 0 ))
5162 err = GetLastError();
5163 goto error;
5165 if ((err = wait_for_completion( request ))) goto error;
5166 if (sa) SafeArrayUnaccessData( sa );
5167 else free( ptr );
5168 request->state = REQUEST_STATE_SENT;
5169 return S_OK;
5171 error:
5172 if (sa) SafeArrayUnaccessData( sa );
5173 else free( ptr );
5174 return HRESULT_FROM_WIN32( err );
5177 static void CALLBACK send_and_receive_proc( TP_CALLBACK_INSTANCE *instance, void *ctx )
5179 struct winhttp_request *request = (struct winhttp_request *)ctx;
5180 if (request_send( request ) == S_OK) request_receive( request );
5181 SetEvent( request->done );
5184 /* critical section must be held */
5185 static DWORD request_wait( struct winhttp_request *request, DWORD timeout )
5187 HANDLE done = request->done;
5188 DWORD err, ret;
5190 LeaveCriticalSection( &request->cs );
5191 while ((err = MsgWaitForMultipleObjects( 1, &done, FALSE, timeout, QS_ALLINPUT )) == WAIT_OBJECT_0 + 1)
5193 MSG msg;
5194 while (PeekMessageW( &msg, NULL, 0, 0, PM_REMOVE ))
5196 TranslateMessage( &msg );
5197 DispatchMessageW( &msg );
5200 switch (err)
5202 case WAIT_OBJECT_0:
5203 ret = request->error;
5204 break;
5205 case WAIT_TIMEOUT:
5206 ret = ERROR_TIMEOUT;
5207 break;
5208 default:
5209 ret = GetLastError();
5210 break;
5212 EnterCriticalSection( &request->cs );
5213 if (err == WAIT_OBJECT_0) request->proc_running = FALSE;
5214 return ret;
5217 static HRESULT WINAPI winhttp_request_Send(
5218 IWinHttpRequest *iface,
5219 VARIANT body )
5221 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5222 HRESULT hr;
5224 TRACE("%p, %s\n", request, debugstr_variant(&body));
5226 EnterCriticalSection( &request->cs );
5227 if (request->state < REQUEST_STATE_OPEN)
5229 LeaveCriticalSection( &request->cs );
5230 return HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN );
5232 if (request->state >= REQUEST_STATE_SENT)
5234 LeaveCriticalSection( &request->cs );
5235 return S_OK;
5237 VariantClear( &request->data );
5238 if ((hr = VariantCopyInd( &request->data, &body )) != S_OK)
5240 LeaveCriticalSection( &request->cs );
5241 return hr;
5243 if (!TrySubmitThreadpoolCallback( send_and_receive_proc, request, NULL ))
5245 LeaveCriticalSection( &request->cs );
5246 return HRESULT_FROM_WIN32( GetLastError() );
5248 request->proc_running = TRUE;
5249 if (!request->async)
5251 hr = HRESULT_FROM_WIN32( request_wait( request, INFINITE ) );
5253 LeaveCriticalSection( &request->cs );
5254 return hr;
5257 static HRESULT WINAPI winhttp_request_get_Status(
5258 IWinHttpRequest *iface,
5259 LONG *status )
5261 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5262 DWORD err = ERROR_SUCCESS, flags, status_code, len = sizeof(status_code), index = 0;
5264 TRACE("%p, %p\n", request, status);
5266 if (!status) return E_INVALIDARG;
5268 EnterCriticalSection( &request->cs );
5269 if (request->state < REQUEST_STATE_SENT)
5271 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5272 goto done;
5274 flags = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER;
5275 if (!WinHttpQueryHeaders( request->hrequest, flags, NULL, &status_code, &len, &index ))
5277 err = GetLastError();
5278 goto done;
5280 *status = status_code;
5282 done:
5283 LeaveCriticalSection( &request->cs );
5284 return HRESULT_FROM_WIN32( err );
5287 static HRESULT WINAPI winhttp_request_get_StatusText(
5288 IWinHttpRequest *iface,
5289 BSTR *status )
5291 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5292 DWORD err = ERROR_SUCCESS, len = 0, index = 0;
5294 TRACE("%p, %p\n", request, status);
5296 if (!status) return E_INVALIDARG;
5298 EnterCriticalSection( &request->cs );
5299 if (request->state < REQUEST_STATE_SENT)
5301 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5302 goto done;
5304 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_STATUS_TEXT, NULL, NULL, &len, &index ))
5306 err = GetLastError();
5307 if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
5309 if (!(*status = SysAllocStringLen( NULL, len / sizeof(WCHAR) )))
5311 err = ERROR_OUTOFMEMORY;
5312 goto done;
5314 index = 0;
5315 err = ERROR_SUCCESS;
5316 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_STATUS_TEXT, NULL, *status, &len, &index ))
5318 err = GetLastError();
5319 SysFreeString( *status );
5321 done:
5322 LeaveCriticalSection( &request->cs );
5323 return HRESULT_FROM_WIN32( err );
5326 static DWORD request_get_codepage( struct winhttp_request *request, UINT *codepage )
5328 WCHAR *buffer, *p;
5329 DWORD size;
5331 *codepage = CP_ACP;
5332 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, NULL, &size, NULL ) &&
5333 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5335 if (!(buffer = malloc( size ))) return ERROR_OUTOFMEMORY;
5336 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, buffer, &size, NULL ))
5338 return GetLastError();
5340 if ((p = wcsstr( buffer, L"charset" )))
5342 p += lstrlenW( L"charset" );
5343 while (*p == ' ') p++;
5344 if (*p++ == '=')
5346 while (*p == ' ') p++;
5347 if (!wcsicmp( p, L"utf-8" )) *codepage = CP_UTF8;
5350 free( buffer );
5352 return ERROR_SUCCESS;
5355 static HRESULT WINAPI winhttp_request_get_ResponseText(
5356 IWinHttpRequest *iface,
5357 BSTR *body )
5359 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5360 UINT codepage;
5361 DWORD err = ERROR_SUCCESS;
5362 int len;
5364 TRACE("%p, %p\n", request, body);
5366 if (!body) return E_INVALIDARG;
5368 EnterCriticalSection( &request->cs );
5369 if (request->state < REQUEST_STATE_SENT)
5371 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5372 goto done;
5374 if ((err = request_get_codepage( request, &codepage ))) goto done;
5375 len = MultiByteToWideChar( codepage, 0, request->buffer, request->offset, NULL, 0 );
5376 if (!(*body = SysAllocStringLen( NULL, len )))
5378 err = ERROR_OUTOFMEMORY;
5379 goto done;
5381 MultiByteToWideChar( codepage, 0, request->buffer, request->offset, *body, len );
5382 (*body)[len] = 0;
5384 done:
5385 LeaveCriticalSection( &request->cs );
5386 return HRESULT_FROM_WIN32( err );
5389 static HRESULT WINAPI winhttp_request_get_ResponseBody(
5390 IWinHttpRequest *iface,
5391 VARIANT *body )
5393 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5394 SAFEARRAY *sa;
5395 HRESULT hr;
5396 DWORD err = ERROR_SUCCESS;
5397 char *ptr;
5399 TRACE("%p, %p\n", request, body);
5401 if (!body) return E_INVALIDARG;
5403 EnterCriticalSection( &request->cs );
5404 if (request->state < REQUEST_STATE_SENT)
5406 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5407 goto done;
5409 if (!(sa = SafeArrayCreateVector( VT_UI1, 0, request->offset )))
5411 err = ERROR_OUTOFMEMORY;
5412 goto done;
5414 if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK)
5416 SafeArrayDestroy( sa );
5417 LeaveCriticalSection( &request->cs );
5418 return hr;
5420 memcpy( ptr, request->buffer, request->offset );
5421 if ((hr = SafeArrayUnaccessData( sa )) != S_OK)
5423 SafeArrayDestroy( sa );
5424 LeaveCriticalSection( &request->cs );
5425 return hr;
5427 V_VT( body ) = VT_ARRAY|VT_UI1;
5428 V_ARRAY( body ) = sa;
5430 done:
5431 LeaveCriticalSection( &request->cs );
5432 return HRESULT_FROM_WIN32( err );
5435 struct stream
5437 IStream IStream_iface;
5438 LONG refs;
5439 char *data;
5440 ULARGE_INTEGER pos, size;
5443 static inline struct stream *impl_from_IStream( IStream *iface )
5445 return CONTAINING_RECORD( iface, struct stream, IStream_iface );
5448 static HRESULT WINAPI stream_QueryInterface( IStream *iface, REFIID riid, void **obj )
5450 struct stream *stream = impl_from_IStream( iface );
5452 TRACE("%p, %s, %p\n", stream, debugstr_guid(riid), obj);
5454 if (IsEqualGUID( riid, &IID_IStream ) || IsEqualGUID( riid, &IID_IUnknown ))
5456 *obj = iface;
5458 else
5460 FIXME("interface %s not implemented\n", debugstr_guid(riid));
5461 return E_NOINTERFACE;
5463 IStream_AddRef( iface );
5464 return S_OK;
5467 static ULONG WINAPI stream_AddRef( IStream *iface )
5469 struct stream *stream = impl_from_IStream( iface );
5470 return InterlockedIncrement( &stream->refs );
5473 static ULONG WINAPI stream_Release( IStream *iface )
5475 struct stream *stream = impl_from_IStream( iface );
5476 LONG refs = InterlockedDecrement( &stream->refs );
5477 if (!refs)
5479 free( stream->data );
5480 free( stream );
5482 return refs;
5485 static HRESULT WINAPI stream_Read( IStream *iface, void *buf, ULONG len, ULONG *read )
5487 struct stream *stream = impl_from_IStream( iface );
5488 ULONG size;
5490 if (stream->pos.QuadPart >= stream->size.QuadPart)
5492 *read = 0;
5493 return S_FALSE;
5496 size = min( stream->size.QuadPart - stream->pos.QuadPart, len );
5497 memcpy( buf, stream->data + stream->pos.QuadPart, size );
5498 stream->pos.QuadPart += size;
5499 *read = size;
5501 return S_OK;
5504 static HRESULT WINAPI stream_Write( IStream *iface, const void *buf, ULONG len, ULONG *written )
5506 FIXME("\n");
5507 return E_NOTIMPL;
5510 static HRESULT WINAPI stream_Seek( IStream *iface, LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *newpos )
5512 struct stream *stream = impl_from_IStream( iface );
5514 if (origin == STREAM_SEEK_SET)
5515 stream->pos.QuadPart = move.QuadPart;
5516 else if (origin == STREAM_SEEK_CUR)
5517 stream->pos.QuadPart += move.QuadPart;
5518 else if (origin == STREAM_SEEK_END)
5519 stream->pos.QuadPart = stream->size.QuadPart - move.QuadPart;
5521 if (newpos) newpos->QuadPart = stream->pos.QuadPart;
5522 return S_OK;
5525 static HRESULT WINAPI stream_SetSize( IStream *iface, ULARGE_INTEGER newsize )
5527 FIXME("\n");
5528 return E_NOTIMPL;
5531 static HRESULT WINAPI stream_CopyTo( IStream *iface, IStream *stream, ULARGE_INTEGER len, ULARGE_INTEGER *read,
5532 ULARGE_INTEGER *written )
5534 FIXME("\n");
5535 return E_NOTIMPL;
5538 static HRESULT WINAPI stream_Commit( IStream *iface, DWORD flags )
5540 FIXME("\n");
5541 return E_NOTIMPL;
5544 static HRESULT WINAPI stream_Revert( IStream *iface )
5546 FIXME("\n");
5547 return E_NOTIMPL;
5550 static HRESULT WINAPI stream_LockRegion( IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER len, DWORD locktype )
5552 FIXME("\n");
5553 return E_NOTIMPL;
5556 static HRESULT WINAPI stream_UnlockRegion( IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER len, DWORD locktype )
5558 FIXME("\n");
5559 return E_NOTIMPL;
5562 static HRESULT WINAPI stream_Stat( IStream *iface, STATSTG *stg, DWORD flag )
5564 FIXME("\n");
5565 return E_NOTIMPL;
5568 static HRESULT WINAPI stream_Clone( IStream *iface, IStream **stream )
5570 FIXME("\n");
5571 return E_NOTIMPL;
5574 static const IStreamVtbl stream_vtbl =
5576 stream_QueryInterface,
5577 stream_AddRef,
5578 stream_Release,
5579 stream_Read,
5580 stream_Write,
5581 stream_Seek,
5582 stream_SetSize,
5583 stream_CopyTo,
5584 stream_Commit,
5585 stream_Revert,
5586 stream_LockRegion,
5587 stream_UnlockRegion,
5588 stream_Stat,
5589 stream_Clone
5592 static HRESULT WINAPI winhttp_request_get_ResponseStream(
5593 IWinHttpRequest *iface,
5594 VARIANT *body )
5596 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5597 DWORD err = ERROR_SUCCESS;
5598 struct stream *stream;
5600 TRACE("%p, %p\n", request, body);
5602 if (!body) return E_INVALIDARG;
5604 EnterCriticalSection( &request->cs );
5605 if (request->state < REQUEST_STATE_SENT)
5607 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5608 goto done;
5610 if (!(stream = malloc( sizeof(*stream) )))
5612 err = ERROR_OUTOFMEMORY;
5613 goto done;
5615 stream->IStream_iface.lpVtbl = &stream_vtbl;
5616 stream->refs = 1;
5617 if (!(stream->data = malloc( request->offset )))
5619 free( stream );
5620 err = ERROR_OUTOFMEMORY;
5621 goto done;
5623 memcpy( stream->data, request->buffer, request->offset );
5624 stream->pos.QuadPart = 0;
5625 stream->size.QuadPart = request->offset;
5626 V_VT( body ) = VT_UNKNOWN;
5627 V_UNKNOWN( body ) = (IUnknown *)&stream->IStream_iface;
5629 done:
5630 LeaveCriticalSection( &request->cs );
5631 return HRESULT_FROM_WIN32( err );
5634 static HRESULT WINAPI winhttp_request_get_Option(
5635 IWinHttpRequest *iface,
5636 WinHttpRequestOption option,
5637 VARIANT *value )
5639 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5640 HRESULT hr = S_OK;
5642 TRACE("%p, %u, %p\n", request, option, value);
5644 EnterCriticalSection( &request->cs );
5645 switch (option)
5647 case WinHttpRequestOption_URLCodePage:
5648 V_VT( value ) = VT_I4;
5649 V_I4( value ) = request->url_codepage;
5650 break;
5651 default:
5652 FIXME("unimplemented option %u\n", option);
5653 hr = E_NOTIMPL;
5654 break;
5656 LeaveCriticalSection( &request->cs );
5657 return hr;
5660 static HRESULT WINAPI winhttp_request_put_Option(
5661 IWinHttpRequest *iface,
5662 WinHttpRequestOption option,
5663 VARIANT value )
5665 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5666 HRESULT hr = S_OK;
5668 TRACE("%p, %u, %s\n", request, option, debugstr_variant(&value));
5670 EnterCriticalSection( &request->cs );
5671 switch (option)
5673 case WinHttpRequestOption_EnableRedirects:
5675 if (V_BOOL( &value ))
5676 request->disable_feature &= ~WINHTTP_DISABLE_REDIRECTS;
5677 else
5678 request->disable_feature |= WINHTTP_DISABLE_REDIRECTS;
5679 break;
5681 case WinHttpRequestOption_URLCodePage:
5683 VARIANT cp;
5684 VariantInit( &cp );
5685 hr = VariantChangeType( &cp, &value, 0, VT_UI4 );
5686 if (SUCCEEDED( hr ))
5688 request->url_codepage = V_UI4( &cp );
5689 TRACE("URL codepage: %u\n", request->url_codepage);
5691 else if (V_VT( &value ) == VT_BSTR && !wcsicmp( V_BSTR( &value ), L"utf-8" ))
5693 TRACE("URL codepage: UTF-8\n");
5694 request->url_codepage = CP_UTF8;
5695 hr = S_OK;
5697 else
5698 FIXME("URL codepage %s is not recognized\n", debugstr_variant( &value ));
5699 break;
5701 default:
5702 FIXME("unimplemented option %u\n", option);
5703 hr = E_NOTIMPL;
5704 break;
5706 LeaveCriticalSection( &request->cs );
5707 return hr;
5710 static HRESULT WINAPI winhttp_request_WaitForResponse(
5711 IWinHttpRequest *iface,
5712 VARIANT timeout,
5713 VARIANT_BOOL *succeeded )
5715 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5716 DWORD err, msecs = (V_I4(&timeout) == -1) ? INFINITE : V_I4(&timeout) * 1000;
5718 TRACE("%p, %s, %p\n", request, debugstr_variant(&timeout), succeeded);
5720 EnterCriticalSection( &request->cs );
5721 if (request->state >= REQUEST_STATE_RESPONSE_RECEIVED)
5723 LeaveCriticalSection( &request->cs );
5724 return S_OK;
5726 switch ((err = request_wait( request, msecs )))
5728 case ERROR_TIMEOUT:
5729 if (succeeded) *succeeded = VARIANT_FALSE;
5730 err = ERROR_SUCCESS;
5731 break;
5733 default:
5734 if (succeeded) *succeeded = VARIANT_TRUE;
5735 break;
5737 LeaveCriticalSection( &request->cs );
5738 return HRESULT_FROM_WIN32( err );
5741 static HRESULT WINAPI winhttp_request_Abort(
5742 IWinHttpRequest *iface )
5744 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5746 TRACE("%p\n", request);
5748 EnterCriticalSection( &request->cs );
5749 cancel_request( request );
5750 LeaveCriticalSection( &request->cs );
5751 return S_OK;
5754 static HRESULT WINAPI winhttp_request_SetTimeouts(
5755 IWinHttpRequest *iface,
5756 LONG resolve_timeout,
5757 LONG connect_timeout,
5758 LONG send_timeout,
5759 LONG receive_timeout )
5761 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5763 TRACE( "%p, %ld, %ld, %ld, %ld\n", request, resolve_timeout, connect_timeout, send_timeout, receive_timeout );
5765 EnterCriticalSection( &request->cs );
5766 request->resolve_timeout = resolve_timeout;
5767 request->connect_timeout = connect_timeout;
5768 request->send_timeout = send_timeout;
5769 request->receive_timeout = receive_timeout;
5770 LeaveCriticalSection( &request->cs );
5771 return S_OK;
5774 static HRESULT WINAPI winhttp_request_SetClientCertificate(
5775 IWinHttpRequest *iface,
5776 BSTR certificate )
5778 FIXME("\n");
5779 return E_NOTIMPL;
5782 static HRESULT WINAPI winhttp_request_SetAutoLogonPolicy(
5783 IWinHttpRequest *iface,
5784 WinHttpRequestAutoLogonPolicy policy )
5786 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5787 HRESULT hr = S_OK;
5789 TRACE("%p, %u\n", request, policy );
5791 EnterCriticalSection( &request->cs );
5792 switch (policy)
5794 case AutoLogonPolicy_Always:
5795 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
5796 break;
5797 case AutoLogonPolicy_OnlyIfBypassProxy:
5798 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM;
5799 break;
5800 case AutoLogonPolicy_Never:
5801 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH;
5802 break;
5803 default: hr = E_INVALIDARG;
5804 break;
5806 LeaveCriticalSection( &request->cs );
5807 return hr;
5810 static const struct IWinHttpRequestVtbl winhttp_request_vtbl =
5812 winhttp_request_QueryInterface,
5813 winhttp_request_AddRef,
5814 winhttp_request_Release,
5815 winhttp_request_GetTypeInfoCount,
5816 winhttp_request_GetTypeInfo,
5817 winhttp_request_GetIDsOfNames,
5818 winhttp_request_Invoke,
5819 winhttp_request_SetProxy,
5820 winhttp_request_SetCredentials,
5821 winhttp_request_Open,
5822 winhttp_request_SetRequestHeader,
5823 winhttp_request_GetResponseHeader,
5824 winhttp_request_GetAllResponseHeaders,
5825 winhttp_request_Send,
5826 winhttp_request_get_Status,
5827 winhttp_request_get_StatusText,
5828 winhttp_request_get_ResponseText,
5829 winhttp_request_get_ResponseBody,
5830 winhttp_request_get_ResponseStream,
5831 winhttp_request_get_Option,
5832 winhttp_request_put_Option,
5833 winhttp_request_WaitForResponse,
5834 winhttp_request_Abort,
5835 winhttp_request_SetTimeouts,
5836 winhttp_request_SetClientCertificate,
5837 winhttp_request_SetAutoLogonPolicy
5840 HRESULT WinHttpRequest_create( void **obj )
5842 struct winhttp_request *request;
5844 TRACE("%p\n", obj);
5846 if (!(request = calloc( 1, sizeof(*request) ))) return E_OUTOFMEMORY;
5847 request->IWinHttpRequest_iface.lpVtbl = &winhttp_request_vtbl;
5848 request->refs = 1;
5849 InitializeCriticalSection( &request->cs );
5850 request->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": winhttp_request.cs");
5851 initialize_request( request );
5853 *obj = &request->IWinHttpRequest_iface;
5854 TRACE("returning iface %p\n", *obj);
5855 return S_OK;