dwrite/layout: Constify some internal helpers arguments.
[wine.git] / dlls / winhttp / request.c
blob9f769f3dc35bfcbb514f536071ef98bb5c02aa70
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 FIXME( "unimplemented option %lu\n", option );
3155 SetLastError( ERROR_WINHTTP_INVALID_OPTION );
3156 return FALSE;
3159 static void socket_destroy( struct object_header *hdr )
3161 struct socket *socket = (struct socket *)hdr;
3163 TRACE("%p\n", socket);
3165 stop_queue( &socket->send_q );
3166 stop_queue( &socket->recv_q );
3168 release_object( &socket->request->hdr );
3169 free( socket->send_frame_buffer );
3170 free( socket );
3173 static BOOL socket_set_option( struct object_header *hdr, DWORD option, void *buffer, DWORD buflen )
3175 FIXME( "unimplemented option %lu\n", option );
3176 SetLastError( ERROR_WINHTTP_INVALID_OPTION );
3177 return FALSE;
3180 static const struct object_vtbl socket_vtbl =
3182 socket_handle_closing,
3183 socket_destroy,
3184 socket_query_option,
3185 socket_set_option,
3188 HINTERNET WINAPI WinHttpWebSocketCompleteUpgrade( HINTERNET hrequest, DWORD_PTR context )
3190 struct socket *socket;
3191 struct request *request;
3192 HINTERNET hsocket = NULL;
3194 TRACE( "%p, %Ix\n", hrequest, context );
3196 if (!(request = (struct request *)grab_object( hrequest )))
3198 SetLastError( ERROR_INVALID_HANDLE );
3199 return NULL;
3201 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
3203 release_object( &request->hdr );
3204 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
3205 return NULL;
3207 if (!(socket = calloc( 1, sizeof(*socket) )))
3209 release_object( &request->hdr );
3210 return NULL;
3212 socket->hdr.type = WINHTTP_HANDLE_TYPE_SOCKET;
3213 socket->hdr.vtbl = &socket_vtbl;
3214 socket->hdr.refs = 1;
3215 socket->hdr.callback = request->hdr.callback;
3216 socket->hdr.notify_mask = request->hdr.notify_mask;
3217 socket->hdr.context = context;
3218 InitializeSRWLock( &socket->send_lock );
3219 init_queue( &socket->send_q );
3220 init_queue( &socket->recv_q );
3222 addref_object( &request->hdr );
3223 socket->request = request;
3225 if ((hsocket = alloc_handle( &socket->hdr )))
3227 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, &hsocket, sizeof(hsocket) );
3230 release_object( &socket->hdr );
3231 release_object( &request->hdr );
3232 TRACE("returning %p\n", hsocket);
3233 if (hsocket) SetLastError( ERROR_SUCCESS );
3234 return hsocket;
3237 static DWORD send_bytes( struct socket *socket, char *bytes, int len, int *sent, WSAOVERLAPPED *ovr )
3239 int count;
3240 DWORD err;
3241 err = netconn_send( socket->request->netconn, bytes, len, &count, ovr );
3242 if (sent) *sent = count;
3243 if (err) return err;
3244 return (count == len || (ovr && count)) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR;
3247 #define FIN_BIT (1 << 7)
3248 #define MASK_BIT (1 << 7)
3249 #define RESERVED_BIT (7 << 4)
3250 #define CONTROL_BIT (1 << 3)
3252 static DWORD send_frame( struct socket *socket, enum socket_opcode opcode, USHORT status, const char *buf,
3253 DWORD buflen, BOOL final, WSAOVERLAPPED *ovr )
3255 DWORD i, offset = 2, len = buflen, buffer_size, ret = 0;
3256 int sent_size;
3257 char hdr[14];
3258 char *ptr;
3260 TRACE( "sending %02x frame, len %lu\n", opcode, len );
3262 if (opcode == SOCKET_OPCODE_CLOSE) len += sizeof(status);
3264 hdr[0] = final ? (char)FIN_BIT : 0;
3265 hdr[0] |= opcode;
3266 hdr[1] = (char)MASK_BIT;
3267 if (len < 126) hdr[1] |= len;
3268 else if (len < 65536)
3270 hdr[1] |= 126;
3271 hdr[2] = len >> 8;
3272 hdr[3] = len & 0xff;
3273 offset += 2;
3275 else
3277 hdr[1] |= 127;
3278 hdr[2] = hdr[3] = hdr[4] = hdr[5] = 0;
3279 hdr[6] = len >> 24;
3280 hdr[7] = (len >> 16) & 0xff;
3281 hdr[8] = (len >> 8) & 0xff;
3282 hdr[9] = len & 0xff;
3283 offset += 8;
3286 buffer_size = len + offset + 4;
3287 assert( buffer_size - len < MAX_FRAME_BUFFER_SIZE );
3288 if (buffer_size > socket->send_frame_buffer_size && socket->send_frame_buffer_size < MAX_FRAME_BUFFER_SIZE)
3290 DWORD new_size;
3291 void *new;
3293 new_size = min( buffer_size, MAX_FRAME_BUFFER_SIZE );
3294 if (!(new = realloc( socket->send_frame_buffer, new_size )))
3296 ERR( "out of memory, buffer_size %lu\n", buffer_size);
3297 return ERROR_OUTOFMEMORY;
3299 socket->send_frame_buffer = new;
3300 socket->send_frame_buffer_size = new_size;
3302 ptr = socket->send_frame_buffer;
3304 memcpy(ptr, hdr, offset);
3305 ptr += offset;
3307 RtlGenRandom( socket->mask, 4 );
3308 memcpy( ptr, socket->mask, 4 );
3309 ptr += 4;
3310 socket->mask_index = 0;
3312 if (opcode == SOCKET_OPCODE_CLOSE) /* prepend status code */
3314 *ptr++ = (status >> 8) ^ socket->mask[socket->mask_index++ % 4];
3315 *ptr++ = (status & 0xff) ^ socket->mask[socket->mask_index++ % 4];
3318 offset = ptr - socket->send_frame_buffer;
3319 socket->send_remaining_size = offset + buflen;
3320 socket->client_buffer_offset = 0;
3321 while (socket->send_remaining_size)
3323 len = min( buflen, MAX_FRAME_BUFFER_SIZE - offset );
3324 for (i = 0; i < len; ++i)
3326 socket->send_frame_buffer[offset++] = buf[socket->client_buffer_offset++]
3327 ^ socket->mask[socket->mask_index++ % 4];
3330 sent_size = 0;
3331 ret = send_bytes( socket, socket->send_frame_buffer, offset, &sent_size, ovr );
3332 socket->send_remaining_size -= sent_size;
3333 if (ret)
3335 if (ovr && ret == WSA_IO_PENDING)
3337 memmove( socket->send_frame_buffer, socket->send_frame_buffer + sent_size, offset - sent_size );
3338 socket->bytes_in_send_frame_buffer = offset - sent_size;
3340 return ret;
3342 assert( sent_size == offset );
3343 offset = 0;
3344 buflen -= len;
3346 return ERROR_SUCCESS;
3349 static DWORD complete_send_frame( struct socket *socket, WSAOVERLAPPED *ovr, const char *buf )
3351 DWORD ret, len, i;
3353 if (!netconn_wait_overlapped_result( socket->request->netconn, ovr, &len ))
3354 return WSAGetLastError();
3356 if (socket->bytes_in_send_frame_buffer)
3358 ret = send_bytes( socket, socket->send_frame_buffer, socket->bytes_in_send_frame_buffer, NULL, NULL );
3359 if (ret) return ret;
3362 assert( socket->bytes_in_send_frame_buffer <= socket->send_remaining_size );
3363 socket->send_remaining_size -= socket->bytes_in_send_frame_buffer;
3365 while (socket->send_remaining_size)
3367 len = min( socket->send_remaining_size, MAX_FRAME_BUFFER_SIZE );
3368 for (i = 0; i < len; ++i)
3370 socket->send_frame_buffer[i] = buf[socket->client_buffer_offset++]
3371 ^ socket->mask[socket->mask_index++ % 4];
3373 ret = send_bytes( socket, socket->send_frame_buffer, len, NULL, NULL );
3374 if (ret) return ret;
3375 socket->send_remaining_size -= len;
3377 return ERROR_SUCCESS;
3380 static void send_io_complete( struct object_header *hdr )
3382 LONG count = InterlockedDecrement( &hdr->pending_sends );
3383 assert( count >= 0 );
3386 /* returns FALSE if sending callback should be omitted. */
3387 static void receive_io_complete( struct socket *socket )
3389 LONG count = InterlockedDecrement( &socket->hdr.pending_receives );
3390 assert( count >= 0 );
3393 static BOOL socket_can_send( struct socket *socket )
3395 return socket->state == SOCKET_STATE_OPEN && !socket->close_frame_received;
3398 static BOOL socket_can_receive( struct socket *socket )
3400 return socket->state <= SOCKET_STATE_SHUTDOWN && !socket->close_frame_received;
3403 static BOOL validate_buffer_type( WINHTTP_WEB_SOCKET_BUFFER_TYPE type, enum fragment_type current_fragment )
3405 switch (current_fragment)
3407 case SOCKET_FRAGMENT_NONE:
3408 return type == WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE
3409 || type == WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE
3410 || type == WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE
3411 || type == WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE;
3412 case SOCKET_FRAGMENT_BINARY:
3413 return type == WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE
3414 || type == WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE;
3415 case SOCKET_FRAGMENT_UTF8:
3416 return type == WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE
3417 || type == WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE;
3419 assert( 0 );
3420 return FALSE;
3423 static enum socket_opcode map_buffer_type( struct socket *socket, WINHTTP_WEB_SOCKET_BUFFER_TYPE type )
3425 switch (type)
3427 case WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE:
3428 if (socket->sending_fragment_type)
3430 socket->sending_fragment_type = SOCKET_FRAGMENT_NONE;
3431 return SOCKET_OPCODE_CONTINUE;
3433 return SOCKET_OPCODE_TEXT;
3435 case WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE:
3436 if (socket->sending_fragment_type)
3438 socket->sending_fragment_type = SOCKET_FRAGMENT_NONE;
3439 return SOCKET_OPCODE_CONTINUE;
3441 return SOCKET_OPCODE_BINARY;
3443 case WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE:
3444 if (!socket->sending_fragment_type)
3446 socket->sending_fragment_type = SOCKET_FRAGMENT_UTF8;
3447 return SOCKET_OPCODE_TEXT;
3449 return SOCKET_OPCODE_CONTINUE;
3451 case WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE:
3452 if (!socket->sending_fragment_type)
3454 socket->sending_fragment_type = SOCKET_FRAGMENT_BINARY;
3455 return SOCKET_OPCODE_BINARY;
3457 return SOCKET_OPCODE_CONTINUE;
3459 case WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE:
3460 return SOCKET_OPCODE_CLOSE;
3462 default:
3463 FIXME("buffer type %u not supported\n", type);
3464 return SOCKET_OPCODE_INVALID;
3468 static void socket_send_complete( struct socket *socket, DWORD ret, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, DWORD len )
3470 if (!ret)
3472 WINHTTP_WEB_SOCKET_STATUS status;
3473 status.dwBytesTransferred = len;
3474 status.eBufferType = type;
3475 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, &status, sizeof(status) );
3477 else
3479 WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
3480 result.AsyncResult.dwResult = API_WRITE_DATA;
3481 result.AsyncResult.dwError = ret;
3482 result.Operation = WINHTTP_WEB_SOCKET_SEND_OPERATION;
3483 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
3487 static DWORD socket_send( struct socket *socket, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, const void *buf, DWORD len,
3488 WSAOVERLAPPED *ovr )
3490 enum socket_opcode opcode = map_buffer_type( socket, type );
3491 BOOL final = (type != WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE &&
3492 type != WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE);
3494 return send_frame( socket, opcode, 0, buf, len, final, ovr );
3497 static void task_socket_send( void *ctx, BOOL abort )
3499 struct socket_send *s = ctx;
3500 struct socket *socket = (struct socket *)s->task_hdr.obj;
3501 DWORD ret;
3503 if (abort) return;
3505 TRACE("running %p\n", ctx);
3507 if (s->complete_async) ret = complete_send_frame( socket, &s->ovr, s->buf );
3508 else ret = socket_send( socket, s->type, s->buf, s->len, NULL );
3510 send_io_complete( &socket->hdr );
3511 InterlockedExchange( &socket->pending_noncontrol_send, 0 );
3512 socket_send_complete( socket, ret, s->type, s->len );
3515 DWORD WINAPI WinHttpWebSocketSend( HINTERNET hsocket, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, void *buf, DWORD len )
3517 struct socket *socket;
3518 DWORD ret = 0;
3520 TRACE( "%p, %u, %p, %lu\n", hsocket, type, buf, len );
3522 if (len && !buf) return ERROR_INVALID_PARAMETER;
3524 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
3525 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
3527 release_object( &socket->hdr );
3528 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
3530 if (!socket_can_send( socket ))
3532 release_object( &socket->hdr );
3533 return ERROR_INVALID_OPERATION;
3536 if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
3538 BOOL async_send, complete_async = FALSE;
3539 struct socket_send *s;
3541 if (InterlockedCompareExchange( &socket->pending_noncontrol_send, 1, 0 ))
3543 WARN( "Previous send is still queued.\n" );
3544 release_object( &socket->hdr );
3545 return ERROR_INVALID_OPERATION;
3547 if (!validate_buffer_type( type, socket->sending_fragment_type ))
3549 WARN( "Invalid buffer type %u, sending_fragment_type %u.\n", type, socket->sending_fragment_type );
3550 InterlockedExchange( &socket->pending_noncontrol_send, 0 );
3551 release_object( &socket->hdr );
3552 return ERROR_INVALID_PARAMETER;
3555 if (!(s = malloc( sizeof(*s) )))
3557 InterlockedExchange( &socket->pending_noncontrol_send, 0 );
3558 release_object( &socket->hdr );
3559 return ERROR_OUTOFMEMORY;
3562 AcquireSRWLockExclusive( &socket->send_lock );
3563 async_send = InterlockedIncrement( &socket->hdr.pending_sends ) > 1 || socket->hdr.recursion_count >= 3;
3564 if (!async_send)
3566 memset( &s->ovr, 0, sizeof(s->ovr) );
3567 if ((ret = socket_send( socket, type, buf, len, &s->ovr )) == WSA_IO_PENDING)
3569 async_send = TRUE;
3570 complete_async = TRUE;
3574 if (async_send)
3576 s->complete_async = complete_async;
3577 TRACE("queueing, complete_async %#x.\n", complete_async);
3578 s->type = type;
3579 s->buf = buf;
3580 s->len = len;
3582 if ((ret = queue_task( &socket->send_q, task_socket_send, &s->task_hdr, &socket->hdr )))
3583 free( s );
3585 if (!async_send || ret)
3587 InterlockedDecrement( &socket->hdr.pending_sends );
3588 InterlockedExchange( &socket->pending_noncontrol_send, 0 );
3590 ReleaseSRWLockExclusive( &socket->send_lock );
3591 if (!async_send)
3593 TRACE("sent sync.\n");
3594 free( s );
3595 socket_send_complete( socket, ret, type, len );
3596 ret = ERROR_SUCCESS;
3599 else
3601 if (validate_buffer_type( type, socket->sending_fragment_type ))
3603 ret = socket_send( socket, type, buf, len, NULL );
3605 else
3607 WARN( "Invalid buffer type %u, sending_fragment_type %u.\n", type, socket->sending_fragment_type );
3608 ret = ERROR_INVALID_PARAMETER;
3612 release_object( &socket->hdr );
3613 return ret;
3616 static DWORD receive_bytes( struct socket *socket, char *buf, DWORD len, DWORD *ret_len, BOOL read_full_buffer )
3618 DWORD err, size = 0, needed = len;
3619 char *ptr = buf;
3620 int received;
3622 if (socket->request->read_size)
3624 size = min( needed, socket->request->read_size );
3625 memcpy( ptr, socket->request->read_buf + socket->request->read_pos, size );
3626 remove_data( socket->request, size );
3627 needed -= size;
3628 ptr += size;
3630 while (size != len)
3632 if ((err = netconn_recv( socket->request->netconn, ptr, needed, 0, &received ))) return err;
3633 if (!received) break;
3634 size += received;
3635 if (!read_full_buffer) break;
3636 needed -= received;
3637 ptr += received;
3639 *ret_len = size;
3640 if (size != len && (read_full_buffer || !size)) return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
3641 return ERROR_SUCCESS;
3644 static BOOL is_supported_opcode( enum socket_opcode opcode )
3646 switch (opcode)
3648 case SOCKET_OPCODE_CONTINUE:
3649 case SOCKET_OPCODE_TEXT:
3650 case SOCKET_OPCODE_BINARY:
3651 case SOCKET_OPCODE_CLOSE:
3652 case SOCKET_OPCODE_PING:
3653 case SOCKET_OPCODE_PONG:
3654 return TRUE;
3655 default:
3656 FIXME( "opcode %02x not handled\n", opcode );
3657 return FALSE;
3661 static DWORD receive_frame( struct socket *socket, DWORD *ret_len, enum socket_opcode *opcode, BOOL *final )
3663 DWORD ret, len, count;
3664 char hdr[2];
3666 if ((ret = receive_bytes( socket, hdr, sizeof(hdr), &count, TRUE ))) return ret;
3667 if ((hdr[0] & RESERVED_BIT) || (hdr[1] & MASK_BIT) || !is_supported_opcode( hdr[0] & 0xf ))
3669 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
3671 *opcode = hdr[0] & 0xf;
3672 *final = hdr[0] & FIN_BIT;
3673 TRACE("received %02x frame, final %#x\n", *opcode, *final);
3675 len = hdr[1] & ~MASK_BIT;
3676 if (len == 126)
3678 USHORT len16;
3679 if ((ret = receive_bytes( socket, (char *)&len16, sizeof(len16), &count, TRUE ))) return ret;
3680 len = RtlUshortByteSwap( len16 );
3682 else if (len == 127)
3684 ULONGLONG len64;
3685 if ((ret = receive_bytes( socket, (char *)&len64, sizeof(len64), &count, TRUE ))) return ret;
3686 if ((len64 = RtlUlonglongByteSwap( len64 )) > ~0u) return ERROR_NOT_SUPPORTED;
3687 len = len64;
3690 *ret_len = len;
3691 return ERROR_SUCCESS;
3694 static void task_socket_send_pong( void *ctx, BOOL abort )
3696 struct socket_send *s = ctx;
3697 struct socket *socket = (struct socket *)s->task_hdr.obj;
3699 if (abort) return;
3701 TRACE("running %p\n", ctx);
3703 if (s->complete_async) complete_send_frame( socket, &s->ovr, NULL );
3704 else send_frame( socket, SOCKET_OPCODE_PONG, 0, NULL, 0, TRUE, NULL );
3706 send_io_complete( &socket->hdr );
3709 static DWORD socket_send_pong( struct socket *socket )
3711 if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
3713 BOOL async_send, complete_async = FALSE;
3714 struct socket_send *s;
3715 DWORD ret = 0;
3717 if (!(s = malloc( sizeof(*s) ))) return ERROR_OUTOFMEMORY;
3719 AcquireSRWLockExclusive( &socket->send_lock );
3720 async_send = InterlockedIncrement( &socket->hdr.pending_sends ) > 1;
3721 if (!async_send)
3723 memset( &s->ovr, 0, sizeof(s->ovr) );
3724 if ((ret = send_frame( socket, SOCKET_OPCODE_PONG, 0, NULL, 0, TRUE, &s->ovr )) == WSA_IO_PENDING)
3726 async_send = TRUE;
3727 complete_async = TRUE;
3731 if (async_send)
3733 s->complete_async = complete_async;
3734 if ((ret = queue_task( &socket->send_q, task_socket_send_pong, &s->task_hdr, &socket->hdr )))
3736 InterlockedDecrement( &socket->hdr.pending_sends );
3737 free( s );
3740 else
3742 InterlockedDecrement( &socket->hdr.pending_sends );
3743 free( s );
3745 ReleaseSRWLockExclusive( &socket->send_lock );
3746 return ret;
3748 return send_frame( socket, SOCKET_OPCODE_PONG, 0, NULL, 0, TRUE, NULL );
3751 static DWORD socket_drain( struct socket *socket )
3753 DWORD ret, count;
3755 while (socket->read_size)
3757 char buf[1024];
3758 if ((ret = receive_bytes( socket, buf, min(socket->read_size, sizeof(buf)), &count, TRUE ))) return ret;
3759 socket->read_size -= count;
3761 return ERROR_SUCCESS;
3764 static DWORD receive_close_status( struct socket *socket, DWORD len )
3766 DWORD reason_len, ret;
3768 socket->close_frame_received = TRUE;
3769 if ((len && (len < sizeof(socket->status) || len > sizeof(socket->status) + sizeof(socket->reason))))
3770 return (socket->close_frame_receive_err = ERROR_WINHTTP_INVALID_SERVER_RESPONSE);
3772 if (!len) return (socket->close_frame_receive_err = ERROR_SUCCESS);
3774 reason_len = len - sizeof(socket->status);
3775 if ((ret = receive_bytes( socket, (char *)&socket->status, sizeof(socket->status), &len, TRUE )))
3776 return (socket->close_frame_receive_err = ret);
3777 socket->status = RtlUshortByteSwap( socket->status );
3778 return (socket->close_frame_receive_err
3779 = receive_bytes( socket, socket->reason, reason_len, &socket->reason_len, TRUE ));
3782 static DWORD handle_control_frame( struct socket *socket )
3784 DWORD ret;
3786 TRACE( "opcode %u.\n", socket->opcode );
3788 switch (socket->opcode)
3790 case SOCKET_OPCODE_PING:
3791 return socket_send_pong( socket );
3793 case SOCKET_OPCODE_PONG:
3794 return socket_drain( socket );
3796 case SOCKET_OPCODE_CLOSE:
3797 if (socket->state < SOCKET_STATE_SHUTDOWN)
3798 WARN( "SOCKET_OPCODE_CLOSE received, socket->state %u.\n", socket->state );
3799 if (socket->close_frame_received)
3801 FIXME( "Close frame already received.\n" );
3802 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
3805 ret = receive_close_status( socket, socket->read_size );
3806 socket->read_size = 0;
3807 return ret;
3809 default:
3810 ERR("unhandled control opcode %02x\n", socket->opcode);
3811 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
3814 return ERROR_SUCCESS;
3817 static WINHTTP_WEB_SOCKET_BUFFER_TYPE map_opcode( struct socket *socket, enum socket_opcode opcode, BOOL fragment )
3819 enum fragment_type frag_type = socket->receiving_fragment_type;
3821 switch (opcode)
3823 case SOCKET_OPCODE_TEXT:
3824 if (frag_type && frag_type != SOCKET_FRAGMENT_UTF8)
3825 FIXME( "Received SOCKET_OPCODE_TEXT with prev fragment %u.\n", frag_type );
3826 if (fragment)
3828 socket->receiving_fragment_type = SOCKET_FRAGMENT_UTF8;
3829 return WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE;
3831 socket->receiving_fragment_type = SOCKET_FRAGMENT_NONE;
3832 return WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE;
3834 case SOCKET_OPCODE_BINARY:
3835 if (frag_type && frag_type != SOCKET_FRAGMENT_BINARY)
3836 FIXME( "Received SOCKET_OPCODE_BINARY with prev fragment %u.\n", frag_type );
3837 if (fragment)
3839 socket->receiving_fragment_type = SOCKET_FRAGMENT_BINARY;
3840 return WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE;
3842 socket->receiving_fragment_type = SOCKET_FRAGMENT_NONE;
3843 return WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE;
3845 case SOCKET_OPCODE_CONTINUE:
3846 if (!frag_type)
3848 FIXME( "Received SOCKET_OPCODE_CONTINUE without starting fragment.\n" );
3849 return ~0u;
3851 if (fragment)
3853 return frag_type == SOCKET_FRAGMENT_BINARY ? WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE
3854 : WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE;
3856 socket->receiving_fragment_type = SOCKET_FRAGMENT_NONE;
3857 return frag_type == SOCKET_FRAGMENT_BINARY ? WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE
3858 : WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE;
3860 case SOCKET_OPCODE_CLOSE:
3861 return WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE;
3863 default:
3864 FIXME("opcode %02x not handled\n", opcode);
3865 return ~0u;
3869 static DWORD socket_receive( struct socket *socket, void *buf, DWORD len, DWORD *ret_len,
3870 WINHTTP_WEB_SOCKET_BUFFER_TYPE *ret_type )
3872 BOOL final = socket->last_receive_final;
3873 DWORD count, ret = ERROR_SUCCESS;
3875 if (!socket->read_size)
3877 for (;;)
3879 if (!(ret = receive_frame( socket, &socket->read_size, &socket->opcode, &final )))
3881 if (!(socket->opcode & CONTROL_BIT) || (ret = handle_control_frame( socket ))
3882 || socket->opcode == SOCKET_OPCODE_CLOSE) break;
3884 else if (ret == WSAETIMEDOUT) ret = socket_send_pong( socket );
3885 if (ret) break;
3888 if (!ret)
3890 socket->last_receive_final = final;
3891 ret = receive_bytes( socket, buf, min(len, socket->read_size), &count, FALSE );
3893 if (!ret)
3895 if (count < socket->read_size)
3896 WARN("Short read.\n");
3898 socket->read_size -= count;
3899 *ret_len = count;
3900 *ret_type = map_opcode( socket, socket->opcode, !final || socket->read_size != 0 );
3901 TRACE( "len %lu, *ret_len %lu, *ret_type %u.\n", len, *ret_len, *ret_type );
3902 if (*ret_type == ~0u)
3904 FIXME( "Unexpected opcode %u.\n", socket->opcode );
3905 socket->read_size = 0;
3906 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
3909 return ret;
3912 static void socket_receive_complete( struct socket *socket, DWORD ret, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, DWORD len )
3914 if (!ret)
3916 WINHTTP_WEB_SOCKET_STATUS status;
3917 status.dwBytesTransferred = len;
3918 status.eBufferType = type;
3919 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, &status, sizeof(status) );
3921 else
3923 WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
3924 result.AsyncResult.dwResult = 0;
3925 result.AsyncResult.dwError = ret;
3926 result.Operation = WINHTTP_WEB_SOCKET_RECEIVE_OPERATION;
3927 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
3931 static void task_socket_receive( void *ctx, BOOL abort )
3933 struct socket_receive *r = ctx;
3934 struct socket *socket = (struct socket *)r->task_hdr.obj;
3935 DWORD ret, count;
3936 WINHTTP_WEB_SOCKET_BUFFER_TYPE type;
3938 if (abort)
3940 socket_receive_complete( socket, ERROR_WINHTTP_OPERATION_CANCELLED, 0, 0 );
3941 return;
3944 TRACE("running %p\n", ctx);
3945 ret = socket_receive( socket, r->buf, r->len, &count, &type );
3946 receive_io_complete( socket );
3947 if (task_needs_completion( &r->task_hdr ))
3948 socket_receive_complete( socket, ret, type, count );
3951 DWORD WINAPI WinHttpWebSocketReceive( HINTERNET hsocket, void *buf, DWORD len, DWORD *ret_len,
3952 WINHTTP_WEB_SOCKET_BUFFER_TYPE *ret_type )
3954 struct socket *socket;
3955 DWORD ret;
3957 TRACE( "%p, %p, %lu, %p, %p\n", hsocket, buf, len, ret_len, ret_type );
3959 if (!buf || !len) return ERROR_INVALID_PARAMETER;
3961 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
3962 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
3964 release_object( &socket->hdr );
3965 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
3967 if (!socket_can_receive( socket ))
3969 release_object( &socket->hdr );
3970 return ERROR_INVALID_OPERATION;
3973 if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
3975 struct socket_receive *r;
3977 if (InterlockedIncrement( &socket->hdr.pending_receives ) > 1)
3979 InterlockedDecrement( &socket->hdr.pending_receives );
3980 WARN( "Attempt to queue receive while another is pending.\n" );
3981 release_object( &socket->hdr );
3982 return ERROR_INVALID_OPERATION;
3985 if (!(r = malloc( sizeof(*r) )))
3987 InterlockedDecrement( &socket->hdr.pending_receives );
3988 release_object( &socket->hdr );
3989 return ERROR_OUTOFMEMORY;
3991 r->buf = buf;
3992 r->len = len;
3994 if ((ret = queue_task( &socket->recv_q, task_socket_receive, &r->task_hdr, &socket->hdr )))
3996 InterlockedDecrement( &socket->hdr.pending_receives );
3997 free( r );
4000 else ret = socket_receive( socket, buf, len, ret_len, ret_type );
4002 release_object( &socket->hdr );
4003 return ret;
4006 static void socket_shutdown_complete( struct socket *socket, DWORD ret )
4008 if (!ret) send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE, NULL, 0 );
4009 else
4011 WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
4012 result.AsyncResult.dwResult = API_WRITE_DATA;
4013 result.AsyncResult.dwError = ret;
4014 result.Operation = WINHTTP_WEB_SOCKET_SHUTDOWN_OPERATION;
4015 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
4019 static void task_socket_shutdown( void *ctx, BOOL abort )
4021 struct socket_shutdown *s = ctx;
4022 struct socket *socket = (struct socket *)s->task_hdr.obj;
4023 DWORD ret;
4025 if (abort) return;
4027 TRACE("running %p\n", ctx);
4029 if (s->complete_async) ret = complete_send_frame( socket, &s->ovr, s->reason );
4030 else ret = send_frame( socket, SOCKET_OPCODE_CLOSE, s->status, s->reason, s->len, TRUE, NULL );
4032 send_io_complete( &socket->hdr );
4033 if (s->send_callback) socket_shutdown_complete( socket, ret );
4036 static DWORD send_socket_shutdown( struct socket *socket, USHORT status, const void *reason, DWORD len,
4037 BOOL send_callback)
4039 DWORD ret;
4041 if (socket->state < SOCKET_STATE_SHUTDOWN) socket->state = SOCKET_STATE_SHUTDOWN;
4043 if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
4045 BOOL async_send, complete_async = FALSE;
4046 struct socket_shutdown *s;
4048 if (!(s = malloc( sizeof(*s) ))) return FALSE;
4050 AcquireSRWLockExclusive( &socket->send_lock );
4051 async_send = InterlockedIncrement( &socket->hdr.pending_sends ) > 1 || socket->hdr.recursion_count >= 3;
4052 if (!async_send)
4054 memset( &s->ovr, 0, sizeof(s->ovr) );
4055 if ((ret = send_frame( socket, SOCKET_OPCODE_CLOSE, status, reason, len, TRUE, &s->ovr )) == WSA_IO_PENDING)
4057 async_send = TRUE;
4058 complete_async = TRUE;
4062 if (async_send)
4064 s->complete_async = complete_async;
4065 s->status = status;
4066 memcpy( s->reason, reason, len );
4067 s->len = len;
4068 s->send_callback = send_callback;
4070 if ((ret = queue_task( &socket->send_q, task_socket_shutdown, &s->task_hdr, &socket->hdr )))
4072 InterlockedDecrement( &socket->hdr.pending_sends );
4073 free( s );
4076 else InterlockedDecrement( &socket->hdr.pending_sends );
4077 ReleaseSRWLockExclusive( &socket->send_lock );
4078 if (!async_send)
4080 free( s );
4081 if (send_callback)
4083 socket_shutdown_complete( socket, ret );
4084 ret = ERROR_SUCCESS;
4088 else ret = send_frame( socket, SOCKET_OPCODE_CLOSE, status, reason, len, TRUE, NULL );
4090 return ret;
4093 DWORD WINAPI WinHttpWebSocketShutdown( HINTERNET hsocket, USHORT status, void *reason, DWORD len )
4095 struct socket *socket;
4096 DWORD ret;
4098 TRACE( "%p, %u, %p, %lu\n", hsocket, status, reason, len );
4100 if ((len && !reason) || len > sizeof(socket->reason)) return ERROR_INVALID_PARAMETER;
4102 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
4103 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
4105 release_object( &socket->hdr );
4106 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
4108 if (socket->state >= SOCKET_STATE_SHUTDOWN)
4110 release_object( &socket->hdr );
4111 return ERROR_INVALID_OPERATION;
4114 ret = send_socket_shutdown( socket, status, reason, len, TRUE );
4115 release_object( &socket->hdr );
4116 return ret;
4119 static DWORD socket_close( struct socket *socket )
4121 BOOL final = FALSE;
4122 DWORD ret, count;
4124 if (socket->close_frame_received) return socket->close_frame_receive_err;
4126 if ((ret = socket_drain( socket ))) return ret;
4128 while (1)
4130 if ((ret = receive_frame( socket, &count, &socket->opcode, &final ))) return ret;
4131 if (socket->opcode == SOCKET_OPCODE_CLOSE) break;
4133 socket->read_size = count;
4134 if ((ret = socket_drain( socket ))) return ret;
4136 if (!final)
4137 FIXME( "Received close opcode without FIN bit.\n" );
4139 return receive_close_status( socket, count );
4142 static void socket_close_complete( struct socket *socket, DWORD ret )
4144 if (!ret) send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE, NULL, 0 );
4145 else
4147 WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
4148 result.AsyncResult.dwResult = API_READ_DATA; /* FIXME */
4149 result.AsyncResult.dwError = ret;
4150 result.Operation = WINHTTP_WEB_SOCKET_CLOSE_OPERATION;
4151 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
4155 static void task_socket_close( void *ctx, BOOL abort )
4157 struct socket_shutdown *s = ctx;
4158 struct socket *socket = (struct socket *)s->task_hdr.obj;
4159 DWORD ret;
4161 if (abort)
4163 socket_close_complete( socket, ERROR_WINHTTP_OPERATION_CANCELLED );
4164 return;
4167 TRACE("running %p\n", ctx);
4169 ret = socket_close( socket );
4170 receive_io_complete( socket );
4171 if (task_needs_completion( &s->task_hdr ))
4172 socket_close_complete( socket, ret );
4175 DWORD WINAPI WinHttpWebSocketClose( HINTERNET hsocket, USHORT status, void *reason, DWORD len )
4177 enum socket_state prev_state;
4178 LONG pending_receives = 0;
4179 struct socket *socket;
4180 DWORD ret;
4182 TRACE( "%p, %u, %p, %lu\n", hsocket, status, reason, len );
4184 if ((len && !reason) || len > sizeof(socket->reason)) return ERROR_INVALID_PARAMETER;
4186 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
4187 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
4189 release_object( &socket->hdr );
4190 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
4192 if (socket->state >= SOCKET_STATE_CLOSED)
4194 release_object( &socket->hdr );
4195 return ERROR_INVALID_OPERATION;
4198 prev_state = socket->state;
4199 socket->state = SOCKET_STATE_CLOSED;
4201 if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
4203 pending_receives = InterlockedIncrement( &socket->hdr.pending_receives );
4204 cancel_queue( &socket->recv_q );
4207 if (prev_state < SOCKET_STATE_SHUTDOWN && (ret = send_socket_shutdown( socket, status, reason, len, FALSE )))
4208 goto done;
4210 if (pending_receives == 1 && socket->close_frame_received)
4212 if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
4213 socket_close_complete( socket, socket->close_frame_receive_err );
4214 goto done;
4217 if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
4219 struct socket_shutdown *s;
4221 if (!(s = calloc( 1, sizeof(*s) )))
4223 ret = ERROR_OUTOFMEMORY;
4224 goto done;
4226 if ((ret = queue_task( &socket->recv_q, task_socket_close, &s->task_hdr, &socket->hdr )))
4228 InterlockedDecrement( &socket->hdr.pending_receives );
4229 free( s );
4232 else ret = socket_close( socket );
4234 done:
4235 release_object( &socket->hdr );
4236 return ret;
4239 DWORD WINAPI WinHttpWebSocketQueryCloseStatus( HINTERNET hsocket, USHORT *status, void *reason, DWORD len,
4240 DWORD *ret_len )
4242 struct socket *socket;
4243 DWORD ret;
4245 TRACE( "%p, %p, %p, %lu, %p\n", hsocket, status, reason, len, ret_len );
4247 if (!status || (len && !reason) || !ret_len) return ERROR_INVALID_PARAMETER;
4249 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
4250 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
4252 release_object( &socket->hdr );
4253 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
4256 if (!socket->close_frame_received || socket->close_frame_receive_err)
4258 ret = socket->close_frame_received ? socket->close_frame_receive_err : ERROR_INVALID_OPERATION;
4259 release_object( &socket->hdr );
4260 return ret;
4262 *status = socket->status;
4263 *ret_len = socket->reason_len;
4264 if (socket->reason_len > len) ret = ERROR_INSUFFICIENT_BUFFER;
4265 else
4267 memcpy( reason, socket->reason, socket->reason_len );
4268 ret = ERROR_SUCCESS;
4271 release_object( &socket->hdr );
4272 return ret;
4275 enum request_state
4277 REQUEST_STATE_INITIALIZED,
4278 REQUEST_STATE_CANCELLED,
4279 REQUEST_STATE_OPEN,
4280 REQUEST_STATE_SENT,
4281 REQUEST_STATE_RESPONSE_RECEIVED
4284 struct winhttp_request
4286 IWinHttpRequest IWinHttpRequest_iface;
4287 LONG refs;
4288 CRITICAL_SECTION cs;
4289 enum request_state state;
4290 HINTERNET hsession;
4291 HINTERNET hconnect;
4292 HINTERNET hrequest;
4293 VARIANT data;
4294 WCHAR *verb;
4295 HANDLE done;
4296 HANDLE wait;
4297 HANDLE cancel;
4298 BOOL proc_running;
4299 char *buffer;
4300 DWORD offset;
4301 DWORD bytes_available;
4302 DWORD bytes_read;
4303 DWORD error;
4304 DWORD logon_policy;
4305 DWORD disable_feature;
4306 LONG resolve_timeout;
4307 LONG connect_timeout;
4308 LONG send_timeout;
4309 LONG receive_timeout;
4310 WINHTTP_PROXY_INFO proxy;
4311 BOOL async;
4312 UINT url_codepage;
4315 static inline struct winhttp_request *impl_from_IWinHttpRequest( IWinHttpRequest *iface )
4317 return CONTAINING_RECORD( iface, struct winhttp_request, IWinHttpRequest_iface );
4320 static ULONG WINAPI winhttp_request_AddRef(
4321 IWinHttpRequest *iface )
4323 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4324 return InterlockedIncrement( &request->refs );
4327 /* critical section must be held */
4328 static void cancel_request( struct winhttp_request *request )
4330 if (request->state <= REQUEST_STATE_CANCELLED) return;
4332 if (request->proc_running)
4334 SetEvent( request->cancel );
4335 LeaveCriticalSection( &request->cs );
4337 WaitForSingleObject( request->done, INFINITE );
4339 EnterCriticalSection( &request->cs );
4341 request->state = REQUEST_STATE_CANCELLED;
4344 /* critical section must be held */
4345 static void free_request( struct winhttp_request *request )
4347 if (request->state < REQUEST_STATE_INITIALIZED) return;
4348 WinHttpCloseHandle( request->hrequest );
4349 WinHttpCloseHandle( request->hconnect );
4350 WinHttpCloseHandle( request->hsession );
4351 CloseHandle( request->done );
4352 CloseHandle( request->wait );
4353 CloseHandle( request->cancel );
4354 free( request->proxy.lpszProxy );
4355 free( request->proxy.lpszProxyBypass );
4356 free( request->buffer );
4357 free( request->verb );
4358 VariantClear( &request->data );
4361 static ULONG WINAPI winhttp_request_Release(
4362 IWinHttpRequest *iface )
4364 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4365 LONG refs = InterlockedDecrement( &request->refs );
4366 if (!refs)
4368 TRACE("destroying %p\n", request);
4370 EnterCriticalSection( &request->cs );
4371 cancel_request( request );
4372 free_request( request );
4373 LeaveCriticalSection( &request->cs );
4374 request->cs.DebugInfo->Spare[0] = 0;
4375 DeleteCriticalSection( &request->cs );
4376 free( request );
4378 return refs;
4381 static HRESULT WINAPI winhttp_request_QueryInterface(
4382 IWinHttpRequest *iface,
4383 REFIID riid,
4384 void **obj )
4386 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4388 TRACE("%p, %s, %p\n", request, debugstr_guid(riid), obj );
4390 if (IsEqualGUID( riid, &IID_IWinHttpRequest ) ||
4391 IsEqualGUID( riid, &IID_IDispatch ) ||
4392 IsEqualGUID( riid, &IID_IUnknown ))
4394 *obj = iface;
4396 else
4398 FIXME("interface %s not implemented\n", debugstr_guid(riid));
4399 return E_NOINTERFACE;
4401 IWinHttpRequest_AddRef( iface );
4402 return S_OK;
4405 static HRESULT WINAPI winhttp_request_GetTypeInfoCount(
4406 IWinHttpRequest *iface,
4407 UINT *count )
4409 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4411 TRACE("%p, %p\n", request, count);
4412 *count = 1;
4413 return S_OK;
4416 enum type_id
4418 IWinHttpRequest_tid,
4419 last_tid
4422 static ITypeLib *winhttp_typelib;
4423 static ITypeInfo *winhttp_typeinfo[last_tid];
4425 static REFIID winhttp_tid_id[] =
4427 &IID_IWinHttpRequest
4430 static HRESULT get_typeinfo( enum type_id tid, ITypeInfo **ret )
4432 HRESULT hr;
4434 if (!winhttp_typelib)
4436 ITypeLib *typelib;
4438 hr = LoadRegTypeLib( &LIBID_WinHttp, 5, 1, LOCALE_SYSTEM_DEFAULT, &typelib );
4439 if (FAILED(hr))
4441 ERR( "LoadRegTypeLib failed: %#lx\n", hr );
4442 return hr;
4444 if (InterlockedCompareExchangePointer( (void **)&winhttp_typelib, typelib, NULL ))
4445 ITypeLib_Release( typelib );
4447 if (!winhttp_typeinfo[tid])
4449 ITypeInfo *typeinfo;
4451 hr = ITypeLib_GetTypeInfoOfGuid( winhttp_typelib, winhttp_tid_id[tid], &typeinfo );
4452 if (FAILED(hr))
4454 ERR( "GetTypeInfoOfGuid(%s) failed: %#lx\n", debugstr_guid(winhttp_tid_id[tid]), hr );
4455 return hr;
4457 if (InterlockedCompareExchangePointer( (void **)(winhttp_typeinfo + tid), typeinfo, NULL ))
4458 ITypeInfo_Release( typeinfo );
4460 *ret = winhttp_typeinfo[tid];
4461 ITypeInfo_AddRef(winhttp_typeinfo[tid]);
4462 return S_OK;
4465 void release_typelib(void)
4467 unsigned i;
4469 for (i = 0; i < ARRAY_SIZE(winhttp_typeinfo); i++)
4470 if (winhttp_typeinfo[i])
4471 ITypeInfo_Release(winhttp_typeinfo[i]);
4473 if (winhttp_typelib)
4474 ITypeLib_Release(winhttp_typelib);
4477 static HRESULT WINAPI winhttp_request_GetTypeInfo(
4478 IWinHttpRequest *iface,
4479 UINT index,
4480 LCID lcid,
4481 ITypeInfo **info )
4483 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4484 TRACE( "%p, %u, %lu, %p\n", request, index, lcid, info );
4486 return get_typeinfo( IWinHttpRequest_tid, info );
4489 static HRESULT WINAPI winhttp_request_GetIDsOfNames(
4490 IWinHttpRequest *iface,
4491 REFIID riid,
4492 LPOLESTR *names,
4493 UINT count,
4494 LCID lcid,
4495 DISPID *dispid )
4497 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4498 ITypeInfo *typeinfo;
4499 HRESULT hr;
4501 TRACE( "%p, %s, %p, %u, %lu, %p\n", request, debugstr_guid(riid), names, count, lcid, dispid );
4503 if (!names || !count || !dispid) return E_INVALIDARG;
4505 hr = get_typeinfo( IWinHttpRequest_tid, &typeinfo );
4506 if (SUCCEEDED(hr))
4508 hr = ITypeInfo_GetIDsOfNames( typeinfo, names, count, dispid );
4509 ITypeInfo_Release( typeinfo );
4511 return hr;
4514 static HRESULT WINAPI winhttp_request_Invoke(
4515 IWinHttpRequest *iface,
4516 DISPID member,
4517 REFIID riid,
4518 LCID lcid,
4519 WORD flags,
4520 DISPPARAMS *params,
4521 VARIANT *result,
4522 EXCEPINFO *excep_info,
4523 UINT *arg_err )
4525 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4526 ITypeInfo *typeinfo;
4527 HRESULT hr;
4529 TRACE( "%p, %ld, %s, %lu, %d, %p, %p, %p, %p\n", request, member, debugstr_guid(riid),
4530 lcid, flags, params, result, excep_info, arg_err );
4532 if (!IsEqualIID( riid, &IID_NULL )) return DISP_E_UNKNOWNINTERFACE;
4534 if (member == DISPID_HTTPREQUEST_OPTION)
4536 VARIANT ret_value, option;
4537 UINT err_pos;
4539 if (!result) result = &ret_value;
4540 if (!arg_err) arg_err = &err_pos;
4542 VariantInit( &option );
4543 VariantInit( result );
4545 if (!flags) return S_OK;
4547 if (flags == DISPATCH_PROPERTYPUT)
4549 hr = DispGetParam( params, 0, VT_I4, &option, arg_err );
4550 if (FAILED(hr)) return hr;
4552 hr = IWinHttpRequest_put_Option( &request->IWinHttpRequest_iface, V_I4( &option ), params->rgvarg[0] );
4553 if (FAILED(hr))
4554 WARN( "put_Option(%ld) failed: %#lx\n", V_I4( &option ), hr );
4555 return hr;
4557 else if (flags & (DISPATCH_PROPERTYGET | DISPATCH_METHOD))
4559 hr = DispGetParam( params, 0, VT_I4, &option, arg_err );
4560 if (FAILED(hr)) return hr;
4562 hr = IWinHttpRequest_get_Option( &request->IWinHttpRequest_iface, V_I4( &option ), result );
4563 if (FAILED(hr))
4564 WARN( "get_Option(%ld) failed: %#lx\n", V_I4( &option ), hr );
4565 return hr;
4568 FIXME("unsupported flags %x\n", flags);
4569 return E_NOTIMPL;
4572 /* fallback to standard implementation */
4574 hr = get_typeinfo( IWinHttpRequest_tid, &typeinfo );
4575 if (SUCCEEDED(hr))
4577 hr = ITypeInfo_Invoke( typeinfo, &request->IWinHttpRequest_iface, member, flags,
4578 params, result, excep_info, arg_err );
4579 ITypeInfo_Release( typeinfo );
4581 return hr;
4584 static HRESULT WINAPI winhttp_request_SetProxy(
4585 IWinHttpRequest *iface,
4586 HTTPREQUEST_PROXY_SETTING proxy_setting,
4587 VARIANT proxy_server,
4588 VARIANT bypass_list )
4590 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4591 DWORD err = ERROR_SUCCESS;
4593 TRACE( "%p, %lu, %s, %s\n", request, proxy_setting, debugstr_variant(&proxy_server),
4594 debugstr_variant(&bypass_list) );
4596 EnterCriticalSection( &request->cs );
4597 switch (proxy_setting)
4599 case HTTPREQUEST_PROXYSETTING_DEFAULT:
4600 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
4601 free( request->proxy.lpszProxy );
4602 free( request->proxy.lpszProxyBypass );
4603 request->proxy.lpszProxy = NULL;
4604 request->proxy.lpszProxyBypass = NULL;
4605 break;
4607 case HTTPREQUEST_PROXYSETTING_DIRECT:
4608 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NO_PROXY;
4609 free( request->proxy.lpszProxy );
4610 free( request->proxy.lpszProxyBypass );
4611 request->proxy.lpszProxy = NULL;
4612 request->proxy.lpszProxyBypass = NULL;
4613 break;
4615 case HTTPREQUEST_PROXYSETTING_PROXY:
4616 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
4617 if (V_VT( &proxy_server ) == VT_BSTR)
4619 free( request->proxy.lpszProxy );
4620 request->proxy.lpszProxy = strdupW( V_BSTR( &proxy_server ) );
4622 if (V_VT( &bypass_list ) == VT_BSTR)
4624 free( request->proxy.lpszProxyBypass );
4625 request->proxy.lpszProxyBypass = strdupW( V_BSTR( &bypass_list ) );
4627 break;
4629 default:
4630 err = ERROR_INVALID_PARAMETER;
4631 break;
4633 LeaveCriticalSection( &request->cs );
4634 return HRESULT_FROM_WIN32( err );
4637 static HRESULT WINAPI winhttp_request_SetCredentials(
4638 IWinHttpRequest *iface,
4639 BSTR username,
4640 BSTR password,
4641 HTTPREQUEST_SETCREDENTIALS_FLAGS flags )
4643 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4644 DWORD target, scheme = WINHTTP_AUTH_SCHEME_BASIC; /* FIXME: query supported schemes */
4645 DWORD err = ERROR_SUCCESS;
4647 TRACE( "%p, %s, %p, %#lx\n", request, debugstr_w(username), password, flags );
4649 EnterCriticalSection( &request->cs );
4650 if (request->state < REQUEST_STATE_OPEN)
4652 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN;
4653 goto done;
4655 switch (flags)
4657 case HTTPREQUEST_SETCREDENTIALS_FOR_SERVER:
4658 target = WINHTTP_AUTH_TARGET_SERVER;
4659 break;
4660 case HTTPREQUEST_SETCREDENTIALS_FOR_PROXY:
4661 target = WINHTTP_AUTH_TARGET_PROXY;
4662 break;
4663 default:
4664 err = ERROR_INVALID_PARAMETER;
4665 goto done;
4667 if (!WinHttpSetCredentials( request->hrequest, target, scheme, username, password, NULL ))
4669 err = GetLastError();
4671 done:
4672 LeaveCriticalSection( &request->cs );
4673 return HRESULT_FROM_WIN32( err );
4676 static void initialize_request( struct winhttp_request *request )
4678 request->wait = CreateEventW( NULL, FALSE, FALSE, NULL );
4679 request->cancel = CreateEventW( NULL, FALSE, FALSE, NULL );
4680 request->done = CreateEventW( NULL, FALSE, FALSE, NULL );
4681 request->connect_timeout = 60000;
4682 request->send_timeout = 30000;
4683 request->receive_timeout = 30000;
4684 request->url_codepage = CP_UTF8;
4685 VariantInit( &request->data );
4686 request->state = REQUEST_STATE_INITIALIZED;
4689 static void reset_request( struct winhttp_request *request )
4691 cancel_request( request );
4692 WinHttpCloseHandle( request->hrequest );
4693 request->hrequest = NULL;
4694 WinHttpCloseHandle( request->hconnect );
4695 request->hconnect = NULL;
4696 free( request->buffer );
4697 request->buffer = NULL;
4698 free( request->verb );
4699 request->verb = NULL;
4700 request->offset = 0;
4701 request->bytes_available = 0;
4702 request->bytes_read = 0;
4703 request->error = ERROR_SUCCESS;
4704 request->logon_policy = 0;
4705 request->disable_feature = 0;
4706 request->async = FALSE;
4707 request->connect_timeout = 60000;
4708 request->send_timeout = 30000;
4709 request->receive_timeout = 30000;
4710 request->url_codepage = CP_UTF8;
4711 free( request->proxy.lpszProxy );
4712 request->proxy.lpszProxy = NULL;
4713 free( request->proxy.lpszProxyBypass );
4714 request->proxy.lpszProxyBypass = NULL;
4715 VariantClear( &request->data );
4716 request->state = REQUEST_STATE_INITIALIZED;
4719 static HRESULT WINAPI winhttp_request_Open(
4720 IWinHttpRequest *iface,
4721 BSTR method,
4722 BSTR url,
4723 VARIANT async )
4725 static const WCHAR httpsW[] = {'h','t','t','p','s'};
4726 static const WCHAR *acceptW[] = {L"*/*", NULL};
4727 static const WCHAR user_agentW[] = L"Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)";
4728 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4729 URL_COMPONENTS uc;
4730 WCHAR *hostname, *path = NULL, *verb = NULL;
4731 DWORD err = ERROR_OUTOFMEMORY, len, flags = 0;
4733 TRACE("%p, %s, %s, %s\n", request, debugstr_w(method), debugstr_w(url),
4734 debugstr_variant(&async));
4736 if (!method || !url) return E_INVALIDARG;
4738 memset( &uc, 0, sizeof(uc) );
4739 uc.dwStructSize = sizeof(uc);
4740 uc.dwSchemeLength = ~0u;
4741 uc.dwHostNameLength = ~0u;
4742 uc.dwUrlPathLength = ~0u;
4743 uc.dwExtraInfoLength = ~0u;
4744 if (!WinHttpCrackUrl( url, 0, 0, &uc )) return HRESULT_FROM_WIN32( GetLastError() );
4746 EnterCriticalSection( &request->cs );
4747 reset_request( request );
4749 if (!(hostname = malloc( (uc.dwHostNameLength + 1) * sizeof(WCHAR) ))) goto error;
4750 memcpy( hostname, uc.lpszHostName, uc.dwHostNameLength * sizeof(WCHAR) );
4751 hostname[uc.dwHostNameLength] = 0;
4753 if (!(path = malloc( (uc.dwUrlPathLength + uc.dwExtraInfoLength + 1) * sizeof(WCHAR) ))) goto error;
4754 memcpy( path, uc.lpszUrlPath, (uc.dwUrlPathLength + uc.dwExtraInfoLength) * sizeof(WCHAR) );
4755 path[uc.dwUrlPathLength + uc.dwExtraInfoLength] = 0;
4757 if (!(verb = strdupW( method ))) goto error;
4758 if (SUCCEEDED( VariantChangeType( &async, &async, 0, VT_BOOL )) && V_BOOL( &async )) request->async = TRUE;
4759 else request->async = FALSE;
4761 if (!request->hsession)
4763 if (!(request->hsession = WinHttpOpen( user_agentW, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, NULL, NULL,
4764 WINHTTP_FLAG_ASYNC )))
4766 err = GetLastError();
4767 goto error;
4769 if (!(request->hconnect = WinHttpConnect( request->hsession, hostname, uc.nPort, 0 )))
4771 WinHttpCloseHandle( request->hsession );
4772 request->hsession = NULL;
4773 err = GetLastError();
4774 goto error;
4777 else if (!(request->hconnect = WinHttpConnect( request->hsession, hostname, uc.nPort, 0 )))
4779 err = GetLastError();
4780 goto error;
4783 len = ARRAY_SIZE( httpsW );
4784 if (uc.dwSchemeLength == len && !memcmp( uc.lpszScheme, httpsW, len * sizeof(WCHAR) ))
4786 flags |= WINHTTP_FLAG_SECURE;
4788 if (!(request->hrequest = WinHttpOpenRequest( request->hconnect, method, path, NULL, NULL, acceptW, flags )))
4790 err = GetLastError();
4791 goto error;
4793 WinHttpSetOption( request->hrequest, WINHTTP_OPTION_CONTEXT_VALUE, &request, sizeof(request) );
4795 request->state = REQUEST_STATE_OPEN;
4796 request->verb = verb;
4797 free( hostname );
4798 free( path );
4799 LeaveCriticalSection( &request->cs );
4800 return S_OK;
4802 error:
4803 WinHttpCloseHandle( request->hconnect );
4804 request->hconnect = NULL;
4805 free( hostname );
4806 free( path );
4807 free( verb );
4808 LeaveCriticalSection( &request->cs );
4809 return HRESULT_FROM_WIN32( err );
4812 static HRESULT WINAPI winhttp_request_SetRequestHeader(
4813 IWinHttpRequest *iface,
4814 BSTR header,
4815 BSTR value )
4817 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4818 DWORD len, err = ERROR_SUCCESS;
4819 WCHAR *str;
4821 TRACE("%p, %s, %s\n", request, debugstr_w(header), debugstr_w(value));
4823 if (!header) return E_INVALIDARG;
4825 EnterCriticalSection( &request->cs );
4826 if (request->state < REQUEST_STATE_OPEN)
4828 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN;
4829 goto done;
4831 if (request->state >= REQUEST_STATE_SENT)
4833 err = ERROR_WINHTTP_CANNOT_CALL_AFTER_SEND;
4834 goto done;
4836 len = lstrlenW( header ) + 4;
4837 if (value) len += lstrlenW( value );
4838 if (!(str = malloc( (len + 1) * sizeof(WCHAR) )))
4840 err = ERROR_OUTOFMEMORY;
4841 goto done;
4843 swprintf( str, len + 1, L"%s: %s\r\n", header, value ? value : L"" );
4844 if (!WinHttpAddRequestHeaders( request->hrequest, str, len,
4845 WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE ))
4847 err = GetLastError();
4849 free( str );
4851 done:
4852 LeaveCriticalSection( &request->cs );
4853 return HRESULT_FROM_WIN32( err );
4856 static HRESULT WINAPI winhttp_request_GetResponseHeader(
4857 IWinHttpRequest *iface,
4858 BSTR header,
4859 BSTR *value )
4861 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4862 DWORD size, err = ERROR_SUCCESS;
4864 TRACE("%p, %p\n", request, header);
4866 EnterCriticalSection( &request->cs );
4867 if (request->state < REQUEST_STATE_SENT)
4869 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
4870 goto done;
4872 if (!header || !value)
4874 err = ERROR_INVALID_PARAMETER;
4875 goto done;
4877 size = 0;
4878 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CUSTOM, header, NULL, &size, NULL ))
4880 err = GetLastError();
4881 if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
4883 if (!(*value = SysAllocStringLen( NULL, size / sizeof(WCHAR) )))
4885 err = ERROR_OUTOFMEMORY;
4886 goto done;
4888 err = ERROR_SUCCESS;
4889 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CUSTOM, header, *value, &size, NULL ))
4891 err = GetLastError();
4892 SysFreeString( *value );
4894 done:
4895 LeaveCriticalSection( &request->cs );
4896 return HRESULT_FROM_WIN32( err );
4899 static HRESULT WINAPI winhttp_request_GetAllResponseHeaders(
4900 IWinHttpRequest *iface,
4901 BSTR *headers )
4903 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4904 DWORD size, err = ERROR_SUCCESS;
4906 TRACE("%p, %p\n", request, headers);
4908 if (!headers) return E_INVALIDARG;
4910 EnterCriticalSection( &request->cs );
4911 if (request->state < REQUEST_STATE_SENT)
4913 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
4914 goto done;
4916 size = 0;
4917 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, NULL, &size, NULL ))
4919 err = GetLastError();
4920 if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
4922 if (!(*headers = SysAllocStringLen( NULL, size / sizeof(WCHAR) )))
4924 err = ERROR_OUTOFMEMORY;
4925 goto done;
4927 err = ERROR_SUCCESS;
4928 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, *headers, &size, NULL ))
4930 err = GetLastError();
4931 SysFreeString( *headers );
4933 done:
4934 LeaveCriticalSection( &request->cs );
4935 return HRESULT_FROM_WIN32( err );
4938 static void CALLBACK wait_status_callback( HINTERNET handle, DWORD_PTR context, DWORD status, LPVOID buffer, DWORD size )
4940 struct winhttp_request *request = (struct winhttp_request *)context;
4942 switch (status)
4944 case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
4945 request->bytes_available = *(DWORD *)buffer;
4946 request->error = ERROR_SUCCESS;
4947 break;
4948 case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
4949 request->bytes_read = size;
4950 request->error = ERROR_SUCCESS;
4951 break;
4952 case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
4954 WINHTTP_ASYNC_RESULT *result = (WINHTTP_ASYNC_RESULT *)buffer;
4955 request->error = result->dwError;
4956 break;
4958 default:
4959 request->error = ERROR_SUCCESS;
4960 break;
4962 SetEvent( request->wait );
4965 static void wait_set_status_callback( struct winhttp_request *request, DWORD status )
4967 status |= WINHTTP_CALLBACK_STATUS_REQUEST_ERROR;
4968 WinHttpSetStatusCallback( request->hrequest, wait_status_callback, status, 0 );
4971 static DWORD wait_for_completion( struct winhttp_request *request )
4973 HANDLE handles[2] = { request->wait, request->cancel };
4974 DWORD ret;
4976 switch (WaitForMultipleObjects( 2, handles, FALSE, INFINITE ))
4978 case WAIT_OBJECT_0:
4979 ret = request->error;
4980 break;
4981 case WAIT_OBJECT_0 + 1:
4982 ret = request->error = ERROR_CANCELLED;
4983 SetEvent( request->done );
4984 break;
4985 default:
4986 ret = request->error = GetLastError();
4987 break;
4989 return ret;
4992 static HRESULT request_receive( struct winhttp_request *request )
4994 DWORD err, size, buflen = 4096;
4996 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE );
4997 if (!WinHttpReceiveResponse( request->hrequest, NULL ))
4999 return HRESULT_FROM_WIN32( GetLastError() );
5001 if ((err = wait_for_completion( request ))) return HRESULT_FROM_WIN32( err );
5002 if (!wcscmp( request->verb, L"HEAD" ))
5004 request->state = REQUEST_STATE_RESPONSE_RECEIVED;
5005 return S_OK;
5007 if (!(request->buffer = malloc( buflen ))) return E_OUTOFMEMORY;
5008 request->buffer[0] = 0;
5009 size = 0;
5012 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE );
5013 if (!WinHttpQueryDataAvailable( request->hrequest, &request->bytes_available ))
5015 err = GetLastError();
5016 goto error;
5018 if ((err = wait_for_completion( request ))) goto error;
5019 if (!request->bytes_available) break;
5020 size += request->bytes_available;
5021 if (buflen < size)
5023 char *tmp;
5024 while (buflen < size) buflen *= 2;
5025 if (!(tmp = realloc( request->buffer, buflen )))
5027 err = ERROR_OUTOFMEMORY;
5028 goto error;
5030 request->buffer = tmp;
5032 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_READ_COMPLETE );
5033 if (!WinHttpReadData( request->hrequest, request->buffer + request->offset,
5034 request->bytes_available, &request->bytes_read ))
5036 err = GetLastError();
5037 goto error;
5039 if ((err = wait_for_completion( request ))) goto error;
5040 request->offset += request->bytes_read;
5041 } while (request->bytes_read);
5043 request->state = REQUEST_STATE_RESPONSE_RECEIVED;
5044 return S_OK;
5046 error:
5047 free( request->buffer );
5048 request->buffer = NULL;
5049 return HRESULT_FROM_WIN32( err );
5052 static DWORD request_set_parameters( struct winhttp_request *request )
5054 if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_PROXY, &request->proxy,
5055 sizeof(request->proxy) )) return GetLastError();
5057 if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_AUTOLOGON_POLICY, &request->logon_policy,
5058 sizeof(request->logon_policy) )) return GetLastError();
5060 if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_DISABLE_FEATURE, &request->disable_feature,
5061 sizeof(request->disable_feature) )) return GetLastError();
5063 if (!WinHttpSetTimeouts( request->hrequest,
5064 request->resolve_timeout,
5065 request->connect_timeout,
5066 request->send_timeout,
5067 request->receive_timeout )) return GetLastError();
5068 return ERROR_SUCCESS;
5071 static void request_set_utf8_content_type( struct winhttp_request *request )
5073 WCHAR headerW[64];
5074 int len;
5076 len = swprintf( headerW, ARRAY_SIZE(headerW), L"%s: %s", L"Content-Type", L"text/plain" );
5077 WinHttpAddRequestHeaders( request->hrequest, headerW, len, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW );
5079 len = swprintf( headerW, ARRAY_SIZE(headerW), L"%s: %s", L"Content-Type", L"charset=utf-8" );
5080 WinHttpAddRequestHeaders( request->hrequest, headerW, len, WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON );
5083 static HRESULT request_send( struct winhttp_request *request )
5085 SAFEARRAY *sa = NULL;
5086 VARIANT data;
5087 char *ptr = NULL;
5088 LONG size = 0;
5089 HRESULT hr;
5090 DWORD err;
5092 if ((err = request_set_parameters( request ))) return HRESULT_FROM_WIN32( err );
5093 if (wcscmp( request->verb, L"GET" ))
5095 VariantInit( &data );
5096 if (V_VT( &request->data ) == VT_BSTR)
5098 UINT cp = CP_ACP;
5099 const WCHAR *str = V_BSTR( &request->data );
5100 int i, len = lstrlenW( str );
5102 for (i = 0; i < len; i++)
5104 if (str[i] > 127)
5106 cp = CP_UTF8;
5107 break;
5110 size = WideCharToMultiByte( cp, 0, str, len, NULL, 0, NULL, NULL );
5111 if (!(ptr = malloc( size ))) return E_OUTOFMEMORY;
5112 WideCharToMultiByte( cp, 0, str, len, ptr, size, NULL, NULL );
5113 if (cp == CP_UTF8) request_set_utf8_content_type( request );
5115 else if (VariantChangeType( &data, &request->data, 0, VT_ARRAY|VT_UI1 ) == S_OK)
5117 sa = V_ARRAY( &data );
5118 if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK) return hr;
5119 if ((hr = SafeArrayGetUBound( sa, 1, &size )) != S_OK)
5121 SafeArrayUnaccessData( sa );
5122 return hr;
5124 size++;
5127 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_REQUEST_SENT );
5128 if (!WinHttpSendRequest( request->hrequest, NULL, 0, ptr, size, size, 0 ))
5130 err = GetLastError();
5131 goto error;
5133 if ((err = wait_for_completion( request ))) goto error;
5134 if (sa) SafeArrayUnaccessData( sa );
5135 else free( ptr );
5136 request->state = REQUEST_STATE_SENT;
5137 return S_OK;
5139 error:
5140 if (sa) SafeArrayUnaccessData( sa );
5141 else free( ptr );
5142 return HRESULT_FROM_WIN32( err );
5145 static void CALLBACK send_and_receive_proc( TP_CALLBACK_INSTANCE *instance, void *ctx )
5147 struct winhttp_request *request = (struct winhttp_request *)ctx;
5148 if (request_send( request ) == S_OK) request_receive( request );
5149 SetEvent( request->done );
5152 /* critical section must be held */
5153 static DWORD request_wait( struct winhttp_request *request, DWORD timeout )
5155 HANDLE done = request->done;
5156 DWORD err, ret;
5158 LeaveCriticalSection( &request->cs );
5159 while ((err = MsgWaitForMultipleObjects( 1, &done, FALSE, timeout, QS_ALLINPUT )) == WAIT_OBJECT_0 + 1)
5161 MSG msg;
5162 while (PeekMessageW( &msg, NULL, 0, 0, PM_REMOVE ))
5164 TranslateMessage( &msg );
5165 DispatchMessageW( &msg );
5168 switch (err)
5170 case WAIT_OBJECT_0:
5171 ret = request->error;
5172 break;
5173 case WAIT_TIMEOUT:
5174 ret = ERROR_TIMEOUT;
5175 break;
5176 default:
5177 ret = GetLastError();
5178 break;
5180 EnterCriticalSection( &request->cs );
5181 if (err == WAIT_OBJECT_0) request->proc_running = FALSE;
5182 return ret;
5185 static HRESULT WINAPI winhttp_request_Send(
5186 IWinHttpRequest *iface,
5187 VARIANT body )
5189 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5190 HRESULT hr;
5192 TRACE("%p, %s\n", request, debugstr_variant(&body));
5194 EnterCriticalSection( &request->cs );
5195 if (request->state < REQUEST_STATE_OPEN)
5197 LeaveCriticalSection( &request->cs );
5198 return HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN );
5200 if (request->state >= REQUEST_STATE_SENT)
5202 LeaveCriticalSection( &request->cs );
5203 return S_OK;
5205 VariantClear( &request->data );
5206 if ((hr = VariantCopyInd( &request->data, &body )) != S_OK)
5208 LeaveCriticalSection( &request->cs );
5209 return hr;
5211 if (!TrySubmitThreadpoolCallback( send_and_receive_proc, request, NULL ))
5213 LeaveCriticalSection( &request->cs );
5214 return HRESULT_FROM_WIN32( GetLastError() );
5216 request->proc_running = TRUE;
5217 if (!request->async)
5219 hr = HRESULT_FROM_WIN32( request_wait( request, INFINITE ) );
5221 LeaveCriticalSection( &request->cs );
5222 return hr;
5225 static HRESULT WINAPI winhttp_request_get_Status(
5226 IWinHttpRequest *iface,
5227 LONG *status )
5229 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5230 DWORD err = ERROR_SUCCESS, flags, status_code, len = sizeof(status_code), index = 0;
5232 TRACE("%p, %p\n", request, status);
5234 if (!status) return E_INVALIDARG;
5236 EnterCriticalSection( &request->cs );
5237 if (request->state < REQUEST_STATE_SENT)
5239 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5240 goto done;
5242 flags = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER;
5243 if (!WinHttpQueryHeaders( request->hrequest, flags, NULL, &status_code, &len, &index ))
5245 err = GetLastError();
5246 goto done;
5248 *status = status_code;
5250 done:
5251 LeaveCriticalSection( &request->cs );
5252 return HRESULT_FROM_WIN32( err );
5255 static HRESULT WINAPI winhttp_request_get_StatusText(
5256 IWinHttpRequest *iface,
5257 BSTR *status )
5259 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5260 DWORD err = ERROR_SUCCESS, len = 0, index = 0;
5262 TRACE("%p, %p\n", request, status);
5264 if (!status) return E_INVALIDARG;
5266 EnterCriticalSection( &request->cs );
5267 if (request->state < REQUEST_STATE_SENT)
5269 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5270 goto done;
5272 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_STATUS_TEXT, NULL, NULL, &len, &index ))
5274 err = GetLastError();
5275 if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
5277 if (!(*status = SysAllocStringLen( NULL, len / sizeof(WCHAR) )))
5279 err = ERROR_OUTOFMEMORY;
5280 goto done;
5282 index = 0;
5283 err = ERROR_SUCCESS;
5284 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_STATUS_TEXT, NULL, *status, &len, &index ))
5286 err = GetLastError();
5287 SysFreeString( *status );
5289 done:
5290 LeaveCriticalSection( &request->cs );
5291 return HRESULT_FROM_WIN32( err );
5294 static DWORD request_get_codepage( struct winhttp_request *request, UINT *codepage )
5296 WCHAR *buffer, *p;
5297 DWORD size;
5299 *codepage = CP_ACP;
5300 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, NULL, &size, NULL ) &&
5301 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5303 if (!(buffer = malloc( size ))) return ERROR_OUTOFMEMORY;
5304 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, buffer, &size, NULL ))
5306 return GetLastError();
5308 if ((p = wcsstr( buffer, L"charset" )))
5310 p += lstrlenW( L"charset" );
5311 while (*p == ' ') p++;
5312 if (*p++ == '=')
5314 while (*p == ' ') p++;
5315 if (!wcsicmp( p, L"utf-8" )) *codepage = CP_UTF8;
5318 free( buffer );
5320 return ERROR_SUCCESS;
5323 static HRESULT WINAPI winhttp_request_get_ResponseText(
5324 IWinHttpRequest *iface,
5325 BSTR *body )
5327 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5328 UINT codepage;
5329 DWORD err = ERROR_SUCCESS;
5330 int len;
5332 TRACE("%p, %p\n", request, body);
5334 if (!body) return E_INVALIDARG;
5336 EnterCriticalSection( &request->cs );
5337 if (request->state < REQUEST_STATE_SENT)
5339 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5340 goto done;
5342 if ((err = request_get_codepage( request, &codepage ))) goto done;
5343 len = MultiByteToWideChar( codepage, 0, request->buffer, request->offset, NULL, 0 );
5344 if (!(*body = SysAllocStringLen( NULL, len )))
5346 err = ERROR_OUTOFMEMORY;
5347 goto done;
5349 MultiByteToWideChar( codepage, 0, request->buffer, request->offset, *body, len );
5350 (*body)[len] = 0;
5352 done:
5353 LeaveCriticalSection( &request->cs );
5354 return HRESULT_FROM_WIN32( err );
5357 static HRESULT WINAPI winhttp_request_get_ResponseBody(
5358 IWinHttpRequest *iface,
5359 VARIANT *body )
5361 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5362 SAFEARRAY *sa;
5363 HRESULT hr;
5364 DWORD err = ERROR_SUCCESS;
5365 char *ptr;
5367 TRACE("%p, %p\n", request, body);
5369 if (!body) return E_INVALIDARG;
5371 EnterCriticalSection( &request->cs );
5372 if (request->state < REQUEST_STATE_SENT)
5374 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5375 goto done;
5377 if (!(sa = SafeArrayCreateVector( VT_UI1, 0, request->offset )))
5379 err = ERROR_OUTOFMEMORY;
5380 goto done;
5382 if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK)
5384 SafeArrayDestroy( sa );
5385 LeaveCriticalSection( &request->cs );
5386 return hr;
5388 memcpy( ptr, request->buffer, request->offset );
5389 if ((hr = SafeArrayUnaccessData( sa )) != S_OK)
5391 SafeArrayDestroy( sa );
5392 LeaveCriticalSection( &request->cs );
5393 return hr;
5395 V_VT( body ) = VT_ARRAY|VT_UI1;
5396 V_ARRAY( body ) = sa;
5398 done:
5399 LeaveCriticalSection( &request->cs );
5400 return HRESULT_FROM_WIN32( err );
5403 struct stream
5405 IStream IStream_iface;
5406 LONG refs;
5407 char *data;
5408 ULARGE_INTEGER pos, size;
5411 static inline struct stream *impl_from_IStream( IStream *iface )
5413 return CONTAINING_RECORD( iface, struct stream, IStream_iface );
5416 static HRESULT WINAPI stream_QueryInterface( IStream *iface, REFIID riid, void **obj )
5418 struct stream *stream = impl_from_IStream( iface );
5420 TRACE("%p, %s, %p\n", stream, debugstr_guid(riid), obj);
5422 if (IsEqualGUID( riid, &IID_IStream ) || IsEqualGUID( riid, &IID_IUnknown ))
5424 *obj = iface;
5426 else
5428 FIXME("interface %s not implemented\n", debugstr_guid(riid));
5429 return E_NOINTERFACE;
5431 IStream_AddRef( iface );
5432 return S_OK;
5435 static ULONG WINAPI stream_AddRef( IStream *iface )
5437 struct stream *stream = impl_from_IStream( iface );
5438 return InterlockedIncrement( &stream->refs );
5441 static ULONG WINAPI stream_Release( IStream *iface )
5443 struct stream *stream = impl_from_IStream( iface );
5444 LONG refs = InterlockedDecrement( &stream->refs );
5445 if (!refs)
5447 free( stream->data );
5448 free( stream );
5450 return refs;
5453 static HRESULT WINAPI stream_Read( IStream *iface, void *buf, ULONG len, ULONG *read )
5455 struct stream *stream = impl_from_IStream( iface );
5456 ULONG size;
5458 if (stream->pos.QuadPart >= stream->size.QuadPart)
5460 *read = 0;
5461 return S_FALSE;
5464 size = min( stream->size.QuadPart - stream->pos.QuadPart, len );
5465 memcpy( buf, stream->data + stream->pos.QuadPart, size );
5466 stream->pos.QuadPart += size;
5467 *read = size;
5469 return S_OK;
5472 static HRESULT WINAPI stream_Write( IStream *iface, const void *buf, ULONG len, ULONG *written )
5474 FIXME("\n");
5475 return E_NOTIMPL;
5478 static HRESULT WINAPI stream_Seek( IStream *iface, LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *newpos )
5480 struct stream *stream = impl_from_IStream( iface );
5482 if (origin == STREAM_SEEK_SET)
5483 stream->pos.QuadPart = move.QuadPart;
5484 else if (origin == STREAM_SEEK_CUR)
5485 stream->pos.QuadPart += move.QuadPart;
5486 else if (origin == STREAM_SEEK_END)
5487 stream->pos.QuadPart = stream->size.QuadPart - move.QuadPart;
5489 if (newpos) newpos->QuadPart = stream->pos.QuadPart;
5490 return S_OK;
5493 static HRESULT WINAPI stream_SetSize( IStream *iface, ULARGE_INTEGER newsize )
5495 FIXME("\n");
5496 return E_NOTIMPL;
5499 static HRESULT WINAPI stream_CopyTo( IStream *iface, IStream *stream, ULARGE_INTEGER len, ULARGE_INTEGER *read,
5500 ULARGE_INTEGER *written )
5502 FIXME("\n");
5503 return E_NOTIMPL;
5506 static HRESULT WINAPI stream_Commit( IStream *iface, DWORD flags )
5508 FIXME("\n");
5509 return E_NOTIMPL;
5512 static HRESULT WINAPI stream_Revert( IStream *iface )
5514 FIXME("\n");
5515 return E_NOTIMPL;
5518 static HRESULT WINAPI stream_LockRegion( IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER len, DWORD locktype )
5520 FIXME("\n");
5521 return E_NOTIMPL;
5524 static HRESULT WINAPI stream_UnlockRegion( IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER len, DWORD locktype )
5526 FIXME("\n");
5527 return E_NOTIMPL;
5530 static HRESULT WINAPI stream_Stat( IStream *iface, STATSTG *stg, DWORD flag )
5532 FIXME("\n");
5533 return E_NOTIMPL;
5536 static HRESULT WINAPI stream_Clone( IStream *iface, IStream **stream )
5538 FIXME("\n");
5539 return E_NOTIMPL;
5542 static const IStreamVtbl stream_vtbl =
5544 stream_QueryInterface,
5545 stream_AddRef,
5546 stream_Release,
5547 stream_Read,
5548 stream_Write,
5549 stream_Seek,
5550 stream_SetSize,
5551 stream_CopyTo,
5552 stream_Commit,
5553 stream_Revert,
5554 stream_LockRegion,
5555 stream_UnlockRegion,
5556 stream_Stat,
5557 stream_Clone
5560 static HRESULT WINAPI winhttp_request_get_ResponseStream(
5561 IWinHttpRequest *iface,
5562 VARIANT *body )
5564 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5565 DWORD err = ERROR_SUCCESS;
5566 struct stream *stream;
5568 TRACE("%p, %p\n", request, body);
5570 if (!body) return E_INVALIDARG;
5572 EnterCriticalSection( &request->cs );
5573 if (request->state < REQUEST_STATE_SENT)
5575 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5576 goto done;
5578 if (!(stream = malloc( sizeof(*stream) )))
5580 err = ERROR_OUTOFMEMORY;
5581 goto done;
5583 stream->IStream_iface.lpVtbl = &stream_vtbl;
5584 stream->refs = 1;
5585 if (!(stream->data = malloc( request->offset )))
5587 free( stream );
5588 err = ERROR_OUTOFMEMORY;
5589 goto done;
5591 memcpy( stream->data, request->buffer, request->offset );
5592 stream->pos.QuadPart = 0;
5593 stream->size.QuadPart = request->offset;
5594 V_VT( body ) = VT_UNKNOWN;
5595 V_UNKNOWN( body ) = (IUnknown *)&stream->IStream_iface;
5597 done:
5598 LeaveCriticalSection( &request->cs );
5599 return HRESULT_FROM_WIN32( err );
5602 static HRESULT WINAPI winhttp_request_get_Option(
5603 IWinHttpRequest *iface,
5604 WinHttpRequestOption option,
5605 VARIANT *value )
5607 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5608 HRESULT hr = S_OK;
5610 TRACE("%p, %u, %p\n", request, option, value);
5612 EnterCriticalSection( &request->cs );
5613 switch (option)
5615 case WinHttpRequestOption_URLCodePage:
5616 V_VT( value ) = VT_I4;
5617 V_I4( value ) = request->url_codepage;
5618 break;
5619 default:
5620 FIXME("unimplemented option %u\n", option);
5621 hr = E_NOTIMPL;
5622 break;
5624 LeaveCriticalSection( &request->cs );
5625 return hr;
5628 static HRESULT WINAPI winhttp_request_put_Option(
5629 IWinHttpRequest *iface,
5630 WinHttpRequestOption option,
5631 VARIANT value )
5633 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5634 HRESULT hr = S_OK;
5636 TRACE("%p, %u, %s\n", request, option, debugstr_variant(&value));
5638 EnterCriticalSection( &request->cs );
5639 switch (option)
5641 case WinHttpRequestOption_EnableRedirects:
5643 if (V_BOOL( &value ))
5644 request->disable_feature &= ~WINHTTP_DISABLE_REDIRECTS;
5645 else
5646 request->disable_feature |= WINHTTP_DISABLE_REDIRECTS;
5647 break;
5649 case WinHttpRequestOption_URLCodePage:
5651 VARIANT cp;
5652 VariantInit( &cp );
5653 hr = VariantChangeType( &cp, &value, 0, VT_UI4 );
5654 if (SUCCEEDED( hr ))
5656 request->url_codepage = V_UI4( &cp );
5657 TRACE("URL codepage: %u\n", request->url_codepage);
5659 else if (V_VT( &value ) == VT_BSTR && !wcsicmp( V_BSTR( &value ), L"utf-8" ))
5661 TRACE("URL codepage: UTF-8\n");
5662 request->url_codepage = CP_UTF8;
5663 hr = S_OK;
5665 else
5666 FIXME("URL codepage %s is not recognized\n", debugstr_variant( &value ));
5667 break;
5669 default:
5670 FIXME("unimplemented option %u\n", option);
5671 hr = E_NOTIMPL;
5672 break;
5674 LeaveCriticalSection( &request->cs );
5675 return hr;
5678 static HRESULT WINAPI winhttp_request_WaitForResponse(
5679 IWinHttpRequest *iface,
5680 VARIANT timeout,
5681 VARIANT_BOOL *succeeded )
5683 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5684 DWORD err, msecs = (V_I4(&timeout) == -1) ? INFINITE : V_I4(&timeout) * 1000;
5686 TRACE("%p, %s, %p\n", request, debugstr_variant(&timeout), succeeded);
5688 EnterCriticalSection( &request->cs );
5689 if (request->state >= REQUEST_STATE_RESPONSE_RECEIVED)
5691 LeaveCriticalSection( &request->cs );
5692 return S_OK;
5694 switch ((err = request_wait( request, msecs )))
5696 case ERROR_TIMEOUT:
5697 if (succeeded) *succeeded = VARIANT_FALSE;
5698 err = ERROR_SUCCESS;
5699 break;
5701 default:
5702 if (succeeded) *succeeded = VARIANT_TRUE;
5703 break;
5705 LeaveCriticalSection( &request->cs );
5706 return HRESULT_FROM_WIN32( err );
5709 static HRESULT WINAPI winhttp_request_Abort(
5710 IWinHttpRequest *iface )
5712 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5714 TRACE("%p\n", request);
5716 EnterCriticalSection( &request->cs );
5717 cancel_request( request );
5718 LeaveCriticalSection( &request->cs );
5719 return S_OK;
5722 static HRESULT WINAPI winhttp_request_SetTimeouts(
5723 IWinHttpRequest *iface,
5724 LONG resolve_timeout,
5725 LONG connect_timeout,
5726 LONG send_timeout,
5727 LONG receive_timeout )
5729 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5731 TRACE( "%p, %ld, %ld, %ld, %ld\n", request, resolve_timeout, connect_timeout, send_timeout, receive_timeout );
5733 EnterCriticalSection( &request->cs );
5734 request->resolve_timeout = resolve_timeout;
5735 request->connect_timeout = connect_timeout;
5736 request->send_timeout = send_timeout;
5737 request->receive_timeout = receive_timeout;
5738 LeaveCriticalSection( &request->cs );
5739 return S_OK;
5742 static HRESULT WINAPI winhttp_request_SetClientCertificate(
5743 IWinHttpRequest *iface,
5744 BSTR certificate )
5746 FIXME("\n");
5747 return E_NOTIMPL;
5750 static HRESULT WINAPI winhttp_request_SetAutoLogonPolicy(
5751 IWinHttpRequest *iface,
5752 WinHttpRequestAutoLogonPolicy policy )
5754 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5755 HRESULT hr = S_OK;
5757 TRACE("%p, %u\n", request, policy );
5759 EnterCriticalSection( &request->cs );
5760 switch (policy)
5762 case AutoLogonPolicy_Always:
5763 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
5764 break;
5765 case AutoLogonPolicy_OnlyIfBypassProxy:
5766 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM;
5767 break;
5768 case AutoLogonPolicy_Never:
5769 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH;
5770 break;
5771 default: hr = E_INVALIDARG;
5772 break;
5774 LeaveCriticalSection( &request->cs );
5775 return hr;
5778 static const struct IWinHttpRequestVtbl winhttp_request_vtbl =
5780 winhttp_request_QueryInterface,
5781 winhttp_request_AddRef,
5782 winhttp_request_Release,
5783 winhttp_request_GetTypeInfoCount,
5784 winhttp_request_GetTypeInfo,
5785 winhttp_request_GetIDsOfNames,
5786 winhttp_request_Invoke,
5787 winhttp_request_SetProxy,
5788 winhttp_request_SetCredentials,
5789 winhttp_request_Open,
5790 winhttp_request_SetRequestHeader,
5791 winhttp_request_GetResponseHeader,
5792 winhttp_request_GetAllResponseHeaders,
5793 winhttp_request_Send,
5794 winhttp_request_get_Status,
5795 winhttp_request_get_StatusText,
5796 winhttp_request_get_ResponseText,
5797 winhttp_request_get_ResponseBody,
5798 winhttp_request_get_ResponseStream,
5799 winhttp_request_get_Option,
5800 winhttp_request_put_Option,
5801 winhttp_request_WaitForResponse,
5802 winhttp_request_Abort,
5803 winhttp_request_SetTimeouts,
5804 winhttp_request_SetClientCertificate,
5805 winhttp_request_SetAutoLogonPolicy
5808 HRESULT WinHttpRequest_create( void **obj )
5810 struct winhttp_request *request;
5812 TRACE("%p\n", obj);
5814 if (!(request = calloc( 1, sizeof(*request) ))) return E_OUTOFMEMORY;
5815 request->IWinHttpRequest_iface.lpVtbl = &winhttp_request_vtbl;
5816 request->refs = 1;
5817 InitializeCriticalSection( &request->cs );
5818 request->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": winhttp_request.cs");
5819 initialize_request( request );
5821 *obj = &request->IWinHttpRequest_iface;
5822 TRACE("returning iface %p\n", *obj);
5823 return S_OK;