dxgi: Add IDXGISwapChain2 stubs for D3D11.
[wine.git] / dlls / winhttp / request.c
blob207f1ec5c610691b9b1b4775005b634aa156f69b
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 #define ACTUAL_DEFAULT_RECEIVE_RESPONSE_TIMEOUT 21000
48 static int request_receive_response_timeout( struct request *req )
50 if (req->receive_response_timeout == -1) return ACTUAL_DEFAULT_RECEIVE_RESPONSE_TIMEOUT;
51 return req->receive_response_timeout;
54 static const WCHAR *attribute_table[] =
56 L"Mime-Version", /* WINHTTP_QUERY_MIME_VERSION = 0 */
57 L"Content-Type" , /* WINHTTP_QUERY_CONTENT_TYPE = 1 */
58 L"Content-Transfer-Encoding", /* WINHTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
59 L"Content-ID", /* WINHTTP_QUERY_CONTENT_ID = 3 */
60 NULL, /* WINHTTP_QUERY_CONTENT_DESCRIPTION = 4 */
61 L"Content-Length", /* WINHTTP_QUERY_CONTENT_LENGTH = 5 */
62 L"Content-Language", /* WINHTTP_QUERY_CONTENT_LANGUAGE = 6 */
63 L"Allow", /* WINHTTP_QUERY_ALLOW = 7 */
64 L"Public", /* WINHTTP_QUERY_PUBLIC = 8 */
65 L"Date", /* WINHTTP_QUERY_DATE = 9 */
66 L"Expires", /* WINHTTP_QUERY_EXPIRES = 10 */
67 L"Last-Modified", /* WINHTTP_QUERY_LAST_MODIFIEDcw = 11 */
68 NULL, /* WINHTTP_QUERY_MESSAGE_ID = 12 */
69 L"URI", /* WINHTTP_QUERY_URI = 13 */
70 L"From", /* WINHTTP_QUERY_DERIVED_FROM = 14 */
71 NULL, /* WINHTTP_QUERY_COST = 15 */
72 NULL, /* WINHTTP_QUERY_LINK = 16 */
73 L"Pragma", /* WINHTTP_QUERY_PRAGMA = 17 */
74 NULL, /* WINHTTP_QUERY_VERSION = 18 */
75 L"Status", /* WINHTTP_QUERY_STATUS_CODE = 19 */
76 NULL, /* WINHTTP_QUERY_STATUS_TEXT = 20 */
77 NULL, /* WINHTTP_QUERY_RAW_HEADERS = 21 */
78 NULL, /* WINHTTP_QUERY_RAW_HEADERS_CRLF = 22 */
79 L"Connection", /* WINHTTP_QUERY_CONNECTION = 23 */
80 L"Accept", /* WINHTTP_QUERY_ACCEPT = 24 */
81 L"Accept-Charset", /* WINHTTP_QUERY_ACCEPT_CHARSET = 25 */
82 L"Accept-Encoding", /* WINHTTP_QUERY_ACCEPT_ENCODING = 26 */
83 L"Accept-Language", /* WINHTTP_QUERY_ACCEPT_LANGUAGE = 27 */
84 L"Authorization", /* WINHTTP_QUERY_AUTHORIZATION = 28 */
85 L"Content-Encoding", /* WINHTTP_QUERY_CONTENT_ENCODING = 29 */
86 NULL, /* WINHTTP_QUERY_FORWARDED = 30 */
87 NULL, /* WINHTTP_QUERY_FROM = 31 */
88 L"If-Modified-Since", /* WINHTTP_QUERY_IF_MODIFIED_SINCE = 32 */
89 L"Location", /* WINHTTP_QUERY_LOCATION = 33 */
90 NULL, /* WINHTTP_QUERY_ORIG_URI = 34 */
91 L"Referer", /* WINHTTP_QUERY_REFERER = 35 */
92 L"Retry-After", /* WINHTTP_QUERY_RETRY_AFTER = 36 */
93 L"Server", /* WINHTTP_QUERY_SERVER = 37 */
94 NULL, /* WINHTTP_TITLE = 38 */
95 L"User-Agent", /* WINHTTP_QUERY_USER_AGENT = 39 */
96 L"WWW-Authenticate", /* WINHTTP_QUERY_WWW_AUTHENTICATE = 40 */
97 L"Proxy-Authenticate", /* WINHTTP_QUERY_PROXY_AUTHENTICATE = 41 */
98 L"Accept-Ranges", /* WINHTTP_QUERY_ACCEPT_RANGES = 42 */
99 L"Set-Cookie", /* WINHTTP_QUERY_SET_COOKIE = 43 */
100 L"Cookie", /* WINHTTP_QUERY_COOKIE = 44 */
101 NULL, /* WINHTTP_QUERY_REQUEST_METHOD = 45 */
102 NULL, /* WINHTTP_QUERY_REFRESH = 46 */
103 NULL, /* WINHTTP_QUERY_CONTENT_DISPOSITION = 47 */
104 L"Age", /* WINHTTP_QUERY_AGE = 48 */
105 L"Cache-Control", /* WINHTTP_QUERY_CACHE_CONTROL = 49 */
106 L"Content-Base", /* WINHTTP_QUERY_CONTENT_BASE = 50 */
107 L"Content-Location", /* WINHTTP_QUERY_CONTENT_LOCATION = 51 */
108 L"Content-MD5", /* WINHTTP_QUERY_CONTENT_MD5 = 52 */
109 L"Content-Range", /* WINHTTP_QUERY_CONTENT_RANGE = 53 */
110 L"ETag", /* WINHTTP_QUERY_ETAG = 54 */
111 L"Host", /* WINHTTP_QUERY_HOST = 55 */
112 L"If-Match", /* WINHTTP_QUERY_IF_MATCH = 56 */
113 L"If-None-Match", /* WINHTTP_QUERY_IF_NONE_MATCH = 57 */
114 L"If-Range", /* WINHTTP_QUERY_IF_RANGE = 58 */
115 L"If-Unmodified-Since", /* WINHTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
116 L"Max-Forwards", /* WINHTTP_QUERY_MAX_FORWARDS = 60 */
117 L"Proxy-Authorization", /* WINHTTP_QUERY_PROXY_AUTHORIZATION = 61 */
118 L"Range", /* WINHTTP_QUERY_RANGE = 62 */
119 L"Transfer-Encoding", /* WINHTTP_QUERY_TRANSFER_ENCODING = 63 */
120 L"Upgrade", /* WINHTTP_QUERY_UPGRADE = 64 */
121 L"Vary", /* WINHTTP_QUERY_VARY = 65 */
122 L"Via", /* WINHTTP_QUERY_VIA = 66 */
123 L"Warning", /* WINHTTP_QUERY_WARNING = 67 */
124 L"Expect", /* WINHTTP_QUERY_EXPECT = 68 */
125 L"Proxy-Connection", /* WINHTTP_QUERY_PROXY_CONNECTION = 69 */
126 L"Unless-Modified-Since", /* WINHTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
127 NULL, /* WINHTTP_QUERY_PROXY_SUPPORT = 75 */
128 NULL, /* WINHTTP_QUERY_AUTHENTICATION_INFO = 76 */
129 NULL, /* WINHTTP_QUERY_PASSPORT_URLS = 77 */
130 NULL /* WINHTTP_QUERY_PASSPORT_CONFIG = 78 */
133 void init_queue( struct queue *queue )
135 InitializeSRWLock( &queue->lock );
136 list_init( &queue->queued_tasks );
137 queue->callback_running = FALSE;
140 void stop_queue( struct queue *queue )
142 assert( list_empty( &queue->queued_tasks ));
143 TRACE("stopped %p\n", queue);
146 static void addref_task( struct task_header *task )
148 InterlockedIncrement( &task->refs );
151 static void release_task( struct task_header *task )
153 if (!InterlockedDecrement( &task->refs ))
154 free( task );
157 static struct task_header *get_next_task( struct queue *queue, struct task_header *prev_task )
159 struct task_header *task;
160 struct list *entry;
162 AcquireSRWLockExclusive( &queue->lock );
163 assert( queue->callback_running );
164 if (prev_task)
166 list_remove( &prev_task->entry );
167 release_task( prev_task );
169 if ((entry = list_head( &queue->queued_tasks )))
171 task = LIST_ENTRY( entry, struct task_header, entry );
172 addref_task( task );
174 else
176 task = NULL;
177 queue->callback_running = FALSE;
179 ReleaseSRWLockExclusive( &queue->lock );
180 return task;
183 static void CALLBACK task_callback( TP_CALLBACK_INSTANCE *instance, void *ctx )
185 struct task_header *task, *next_task;
186 struct queue *queue = ctx;
188 TRACE( "instance %p.\n", instance );
190 task = get_next_task( queue, NULL );
191 while (task)
193 task->callback( task, FALSE );
194 /* Queue object may be freed by release_object() unless there is another task referencing it. */
195 next_task = get_next_task( queue, task );
196 release_object( task->obj );
197 release_task( task );
198 task = next_task;
200 TRACE( "instance %p exiting.\n", instance );
203 static DWORD queue_task( struct queue *queue, TASK_CALLBACK task, struct task_header *task_hdr,
204 struct object_header *obj )
206 BOOL callback_running;
208 TRACE("queueing %p in %p\n", task_hdr, queue);
209 task_hdr->callback = task;
210 task_hdr->completion_sent = 0;
211 task_hdr->refs = 1;
212 task_hdr->obj = obj;
213 addref_object( obj );
215 AcquireSRWLockExclusive( &queue->lock );
216 list_add_tail( &queue->queued_tasks, &task_hdr->entry );
217 if (!(callback_running = queue->callback_running))
219 if ((queue->callback_running = TrySubmitThreadpoolCallback( task_callback, queue, NULL )))
220 callback_running = TRUE;
221 else
222 list_remove( &task_hdr->entry );
224 ReleaseSRWLockExclusive( &queue->lock );
226 if (!callback_running)
228 release_object( obj );
229 ERR( "Submiting threadpool callback failed, err %lu.\n", GetLastError() );
230 return ERROR_OUTOFMEMORY;
233 return ERROR_SUCCESS;
236 static BOOL task_needs_completion( struct task_header *task_hdr )
238 return !InterlockedExchange( &task_hdr->completion_sent, 1 );
241 static BOOL cancel_queue( struct queue *queue )
243 struct task_header *task_hdr, *found;
244 BOOL cancelled = FALSE;
246 while (1)
248 AcquireSRWLockExclusive( &queue->lock );
249 found = NULL;
250 LIST_FOR_EACH_ENTRY( task_hdr, &queue->queued_tasks, struct task_header, entry )
252 if (task_needs_completion( task_hdr ))
254 found = task_hdr;
255 addref_task( found );
256 break;
259 ReleaseSRWLockExclusive( &queue->lock );
260 if (!found) break;
261 cancelled = TRUE;
262 found->callback( found, TRUE );
263 release_task( found );
265 return cancelled;
268 static void task_send_callback( void *ctx, BOOL abort )
270 struct send_callback *s = ctx;
272 if (abort) return;
274 TRACE( "running %p\n", ctx );
275 send_callback( s->task_hdr.obj, s->status, s->info, s->buflen );
278 static void free_header( struct header *header )
280 free( header->field );
281 free( header->value );
282 free( header );
285 static BOOL valid_token_char( WCHAR c )
287 if (c < 32 || c == 127) return FALSE;
288 switch (c)
290 case '(': case ')':
291 case '<': case '>':
292 case '@': case ',':
293 case ';': case ':':
294 case '\\': case '\"':
295 case '/': case '[':
296 case ']': case '?':
297 case '=': case '{':
298 case '}': case ' ':
299 case '\t':
300 return FALSE;
301 default:
302 return TRUE;
306 static struct header *parse_header( const WCHAR *string, size_t string_len )
308 const WCHAR *p, *q;
309 struct header *header;
310 int len;
312 p = string;
313 if (!(q = wcschr( p, ':' )))
315 WARN("no ':' in line %s\n", debugstr_w(string));
316 return NULL;
318 if (q == string)
320 WARN("empty field name in line %s\n", debugstr_w(string));
321 return NULL;
323 while (*p != ':')
325 if (!valid_token_char( *p ))
327 WARN("invalid character in field name %s\n", debugstr_w(string));
328 return NULL;
330 p++;
332 len = q - string;
333 if (!(header = calloc( 1, sizeof(*header) ))) return NULL;
334 if (!(header->field = malloc( (len + 1) * sizeof(WCHAR) )))
336 free( header );
337 return NULL;
339 memcpy( header->field, string, len * sizeof(WCHAR) );
340 header->field[len] = 0;
342 q++; /* skip past colon */
343 while (*q == ' ') q++;
344 len = (string + string_len) - q;
346 if (!(header->value = malloc( (len + 1) * sizeof(WCHAR) )))
348 free_header( header );
349 return NULL;
351 memcpy( header->value, q, len * sizeof(WCHAR) );
352 header->value[len] = 0;
354 return header;
357 static int get_header_index( struct request *request, const WCHAR *field, int requested_index, BOOL request_only )
359 int index;
361 TRACE("%s\n", debugstr_w(field));
363 for (index = 0; index < request->num_headers; index++)
365 if (wcsicmp( request->headers[index].field, field )) continue;
366 if (request_only && !request->headers[index].is_request) continue;
367 if (!request_only && request->headers[index].is_request) continue;
369 if (!requested_index) break;
370 requested_index--;
372 if (index >= request->num_headers) index = -1;
373 TRACE("returning %d\n", index);
374 return index;
377 static DWORD insert_header( struct request *request, struct header *header )
379 DWORD count = request->num_headers + 1;
380 struct header *hdrs;
382 if (request->headers)
384 if ((hdrs = realloc( request->headers, sizeof(*header) * count )))
385 memset( &hdrs[count - 1], 0, sizeof(*header) );
387 else hdrs = calloc( 1, sizeof(*header) );
388 if (!hdrs) return ERROR_OUTOFMEMORY;
390 request->headers = hdrs;
391 request->headers[count - 1].field = wcsdup( header->field );
392 request->headers[count - 1].value = wcsdup( header->value );
393 request->headers[count - 1].is_request = header->is_request;
394 request->num_headers = count;
395 return ERROR_SUCCESS;
398 static void delete_header( struct request *request, DWORD index )
400 if (!request->num_headers || index >= request->num_headers) return;
401 request->num_headers--;
403 free( request->headers[index].field );
404 free( request->headers[index].value );
406 memmove( &request->headers[index], &request->headers[index + 1],
407 (request->num_headers - index) * sizeof(struct header) );
408 memset( &request->headers[request->num_headers], 0, sizeof(struct header) );
411 DWORD process_header( struct request *request, const WCHAR *field, const WCHAR *value, DWORD flags, BOOL request_only )
413 int index;
414 struct header hdr;
416 TRACE( "%s: %s %#lx\n", debugstr_w(field), debugstr_w(value), flags );
418 if ((index = get_header_index( request, field, 0, request_only )) >= 0)
420 if (flags & WINHTTP_ADDREQ_FLAG_ADD_IF_NEW) return ERROR_WINHTTP_HEADER_ALREADY_EXISTS;
423 if (flags & WINHTTP_ADDREQ_FLAG_REPLACE)
425 if (index >= 0)
427 delete_header( request, index );
428 if (!value || !value[0]) return ERROR_SUCCESS;
430 else if (!(flags & WINHTTP_ADDREQ_FLAG_ADD)) return ERROR_WINHTTP_HEADER_NOT_FOUND;
432 hdr.field = (LPWSTR)field;
433 hdr.value = (LPWSTR)value;
434 hdr.is_request = request_only;
435 return insert_header( request, &hdr );
437 else if (value)
440 if ((flags & (WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA | WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON)) &&
441 index >= 0)
443 WCHAR *tmp;
444 int len, len_orig, len_value;
445 struct header *header = &request->headers[index];
447 len_orig = lstrlenW( header->value );
448 len_value = lstrlenW( value );
450 len = len_orig + len_value + 2;
451 if (!(tmp = realloc( header->value, (len + 1) * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
452 header->value = tmp;
453 header->value[len_orig++] = (flags & WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA) ? ',' : ';';
454 header->value[len_orig++] = ' ';
456 memcpy( &header->value[len_orig], value, len_value * sizeof(WCHAR) );
457 header->value[len] = 0;
458 return ERROR_SUCCESS;
460 else
462 hdr.field = (LPWSTR)field;
463 hdr.value = (LPWSTR)value;
464 hdr.is_request = request_only;
465 return insert_header( request, &hdr );
469 return ERROR_SUCCESS;
472 DWORD add_request_headers( struct request *request, const WCHAR *headers, DWORD len, DWORD flags )
474 DWORD ret = ERROR_WINHTTP_INVALID_HEADER;
475 struct header *header;
476 const WCHAR *p, *q;
478 if (len == ~0u) len = lstrlenW( headers );
479 if (!len) return ERROR_SUCCESS;
481 p = headers;
484 const WCHAR *end;
486 if (p >= headers + len) break;
488 for (q = p; q < headers + len && *q != '\r' && *q != '\n'; ++q)
490 end = q;
491 while (*q == '\r' || *q == '\n')
492 ++q;
494 if ((header = parse_header( p, end - p )))
496 ret = process_header( request, header->field, header->value, flags, TRUE );
497 free_header( header );
499 p = q;
500 } while (!ret);
502 return ret;
505 /***********************************************************************
506 * WinHttpAddRequestHeaders (winhttp.@)
508 BOOL WINAPI WinHttpAddRequestHeaders( HINTERNET hrequest, const WCHAR *headers, DWORD len, DWORD flags )
510 DWORD ret;
511 struct request *request;
513 TRACE( "%p, %s, %lu, %#lx\n", hrequest, debugstr_wn(headers, len), len, flags );
515 if (!headers || !len)
517 SetLastError( ERROR_INVALID_PARAMETER );
518 return FALSE;
520 if (!(request = (struct request *)grab_object( hrequest )))
522 SetLastError( ERROR_INVALID_HANDLE );
523 return FALSE;
525 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
527 release_object( &request->hdr );
528 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
529 return FALSE;
532 ret = add_request_headers( request, headers, len, flags );
534 release_object( &request->hdr );
535 SetLastError( ret );
536 return !ret;
539 static WCHAR *build_absolute_request_path( struct request *request, const WCHAR **path )
541 const WCHAR *scheme;
542 WCHAR *ret;
543 int len, offset;
545 scheme = (request->netconn ? request->netconn->secure : (request->hdr.flags & WINHTTP_FLAG_SECURE)) ? L"https" : L"http";
547 len = lstrlenW( scheme ) + lstrlenW( request->connect->hostname ) + 4; /* '://' + nul */
548 if (request->connect->hostport) len += 6; /* ':' between host and port, up to 5 for port */
550 len += lstrlenW( request->path );
551 if ((ret = malloc( len * sizeof(WCHAR) )))
553 offset = swprintf( ret, len, L"%s://%s", scheme, request->connect->hostname );
554 if (request->connect->hostport)
556 offset += swprintf( ret + offset, len - offset, L":%u", request->connect->hostport );
558 lstrcpyW( ret + offset, request->path );
559 if (path) *path = ret + offset;
562 return ret;
565 static WCHAR *build_request_string( struct request *request )
567 WCHAR *path, *ret;
568 unsigned int i, len;
570 if (!wcsicmp( request->connect->hostname, request->connect->servername )) path = request->path;
571 else if (!(path = build_absolute_request_path( request, NULL ))) return NULL;
573 len = lstrlenW( request->verb ) + 1 /* ' ' */;
574 len += lstrlenW( path ) + 1 /* ' ' */;
575 len += lstrlenW( request->version );
577 for (i = 0; i < request->num_headers; i++)
579 if (request->headers[i].is_request)
580 len += lstrlenW( request->headers[i].field ) + lstrlenW( request->headers[i].value ) + 4; /* '\r\n: ' */
582 len += 4; /* '\r\n\r\n' */
584 if ((ret = malloc( (len + 1) * sizeof(WCHAR) )))
586 lstrcpyW( ret, request->verb );
587 lstrcatW( ret, L" " );
588 lstrcatW( ret, path );
589 lstrcatW( ret, L" " );
590 lstrcatW( ret, request->version );
592 for (i = 0; i < request->num_headers; i++)
594 if (request->headers[i].is_request)
596 lstrcatW( ret, L"\r\n" );
597 lstrcatW( ret, request->headers[i].field );
598 lstrcatW( ret, L": " );
599 lstrcatW( ret, request->headers[i].value );
602 lstrcatW( ret, L"\r\n\r\n" );
605 if (path != request->path) free( path );
606 return ret;
609 #define QUERY_MODIFIER_MASK (WINHTTP_QUERY_FLAG_REQUEST_HEADERS | WINHTTP_QUERY_FLAG_SYSTEMTIME | WINHTTP_QUERY_FLAG_NUMBER)
611 static DWORD query_headers( struct request *request, DWORD level, const WCHAR *name, void *buffer, DWORD *buflen,
612 DWORD *index )
614 struct header *header = NULL;
615 BOOL request_only;
616 int requested_index, header_index = -1;
617 DWORD attr, len, ret = ERROR_WINHTTP_HEADER_NOT_FOUND;
619 request_only = level & WINHTTP_QUERY_FLAG_REQUEST_HEADERS;
620 requested_index = index ? *index : 0;
622 attr = level & ~QUERY_MODIFIER_MASK;
623 switch (attr)
625 case WINHTTP_QUERY_CUSTOM:
627 header_index = get_header_index( request, name, requested_index, request_only );
628 break;
630 case WINHTTP_QUERY_RAW_HEADERS:
632 WCHAR *headers, *p, *q;
634 if (request_only)
635 headers = build_request_string( request );
636 else
637 headers = request->raw_headers;
639 if (!(p = headers)) return ERROR_OUTOFMEMORY;
640 for (len = 0; *p; p++) if (*p != '\r') len++;
642 if (!buffer || len * sizeof(WCHAR) > *buflen) ret = ERROR_INSUFFICIENT_BUFFER;
643 else
645 for (p = headers, q = buffer; *p; p++, q++)
647 if (*p != '\r') *q = *p;
648 else
650 *q = 0;
651 p++; /* skip '\n' */
654 TRACE("returning data: %s\n", debugstr_wn(buffer, len));
655 if (len) len--;
656 ret = ERROR_SUCCESS;
658 *buflen = len * sizeof(WCHAR);
659 if (request_only) free( headers );
660 return ret;
662 case WINHTTP_QUERY_RAW_HEADERS_CRLF:
664 WCHAR *headers;
666 if (request_only)
667 headers = build_request_string( request );
668 else
669 headers = request->raw_headers;
671 if (!headers) return ERROR_OUTOFMEMORY;
672 len = lstrlenW( headers ) * sizeof(WCHAR);
673 if (!buffer || len + sizeof(WCHAR) > *buflen)
675 len += sizeof(WCHAR);
676 ret = ERROR_INSUFFICIENT_BUFFER;
678 else
680 memcpy( buffer, headers, len + sizeof(WCHAR) );
681 TRACE("returning data: %s\n", debugstr_wn(buffer, len / sizeof(WCHAR)));
682 ret = ERROR_SUCCESS;
684 *buflen = len;
685 if (request_only) free( headers );
686 return ret;
688 case WINHTTP_QUERY_VERSION:
689 len = lstrlenW( request->version ) * sizeof(WCHAR);
690 if (!buffer || len + sizeof(WCHAR) > *buflen)
692 len += sizeof(WCHAR);
693 ret = ERROR_INSUFFICIENT_BUFFER;
695 else
697 lstrcpyW( buffer, request->version );
698 TRACE("returning string: %s\n", debugstr_w(buffer));
699 ret = ERROR_SUCCESS;
701 *buflen = len;
702 return ret;
704 case WINHTTP_QUERY_STATUS_TEXT:
705 len = lstrlenW( request->status_text ) * sizeof(WCHAR);
706 if (!buffer || len + sizeof(WCHAR) > *buflen)
708 len += sizeof(WCHAR);
709 ret = ERROR_INSUFFICIENT_BUFFER;
711 else
713 lstrcpyW( buffer, request->status_text );
714 TRACE("returning string: %s\n", debugstr_w(buffer));
715 ret = ERROR_SUCCESS;
717 *buflen = len;
718 return ret;
720 case WINHTTP_QUERY_REQUEST_METHOD:
721 len = lstrlenW( request->verb ) * sizeof(WCHAR);
722 if (!buffer || len + sizeof(WCHAR) > *buflen)
724 len += sizeof(WCHAR);
725 ret = ERROR_INSUFFICIENT_BUFFER;
727 else
729 lstrcpyW( buffer, request->verb );
730 TRACE("returning string: %s\n", debugstr_w(buffer));
731 ret = ERROR_SUCCESS;
733 *buflen = len;
734 return ret;
736 default:
737 if (attr >= ARRAY_SIZE(attribute_table)) return ERROR_INVALID_PARAMETER;
738 if (!attribute_table[attr])
740 FIXME( "attribute %lu not implemented\n", attr );
741 return ERROR_WINHTTP_HEADER_NOT_FOUND;
743 TRACE("attribute %s\n", debugstr_w(attribute_table[attr]));
744 header_index = get_header_index( request, attribute_table[attr], requested_index, request_only );
745 break;
748 if (header_index >= 0)
750 header = &request->headers[header_index];
752 if (!header || (request_only && !header->is_request)) return ERROR_WINHTTP_HEADER_NOT_FOUND;
753 if (level & WINHTTP_QUERY_FLAG_NUMBER)
755 if (!buffer || sizeof(DWORD) > *buflen) ret = ERROR_INSUFFICIENT_BUFFER;
756 else
758 DWORD *number = buffer;
759 *number = wcstoul( header->value, NULL, 10 );
760 TRACE("returning number: %lu\n", *number);
761 ret = ERROR_SUCCESS;
763 *buflen = sizeof(DWORD);
765 else if (level & WINHTTP_QUERY_FLAG_SYSTEMTIME)
767 SYSTEMTIME *st = buffer;
768 if (!buffer || sizeof(SYSTEMTIME) > *buflen) ret = ERROR_INSUFFICIENT_BUFFER;
769 else if (WinHttpTimeToSystemTime( header->value, st ))
771 TRACE("returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
772 st->wYear, st->wMonth, st->wDay, st->wDayOfWeek,
773 st->wHour, st->wMinute, st->wSecond, st->wMilliseconds);
774 ret = ERROR_SUCCESS;
776 *buflen = sizeof(SYSTEMTIME);
778 else if (header->value)
780 len = lstrlenW( header->value ) * sizeof(WCHAR);
781 if (!buffer || len + sizeof(WCHAR) > *buflen)
783 len += sizeof(WCHAR);
784 ret = ERROR_INSUFFICIENT_BUFFER;
786 else
788 lstrcpyW( buffer, header->value );
789 TRACE("returning string: %s\n", debugstr_w(buffer));
790 ret = ERROR_SUCCESS;
792 *buflen = len;
794 if (!ret && index) *index += 1;
795 return ret;
798 /***********************************************************************
799 * WinHttpQueryHeaders (winhttp.@)
801 BOOL WINAPI WinHttpQueryHeaders( HINTERNET hrequest, DWORD level, const WCHAR *name, void *buffer, DWORD *buflen,
802 DWORD *index )
804 DWORD ret;
805 struct request *request;
807 TRACE( "%p, %#lx, %s, %p, %p, %p\n", hrequest, level, debugstr_w(name), buffer, buflen, index );
809 if (!(request = (struct request *)grab_object( hrequest )))
811 SetLastError( ERROR_INVALID_HANDLE );
812 return FALSE;
814 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
816 release_object( &request->hdr );
817 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
818 return FALSE;
820 if (request->state < REQUEST_RESPONSE_STATE_RESPONSE_RECEIVED && !(level & WINHTTP_QUERY_FLAG_REQUEST_HEADERS)
821 && ((level & ~QUERY_MODIFIER_MASK) != WINHTTP_QUERY_REQUEST_METHOD))
823 release_object( &request->hdr );
824 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_STATE );
825 return FALSE;
828 ret = query_headers( request, level, name, buffer, buflen, index );
830 release_object( &request->hdr );
831 SetLastError( ret );
832 return !ret;
835 static const struct
837 const WCHAR *str;
838 unsigned int len;
839 DWORD scheme;
841 auth_schemes[] =
843 { L"Basic", ARRAY_SIZE(L"Basic") - 1, WINHTTP_AUTH_SCHEME_BASIC },
844 { L"NTLM", ARRAY_SIZE(L"NTLM") - 1, WINHTTP_AUTH_SCHEME_NTLM },
845 { L"Passport", ARRAY_SIZE(L"Passport") - 1, WINHTTP_AUTH_SCHEME_PASSPORT },
846 { L"Digest", ARRAY_SIZE(L"Digest") - 1, WINHTTP_AUTH_SCHEME_DIGEST },
847 { L"Negotiate", ARRAY_SIZE(L"Negotiate") - 1, WINHTTP_AUTH_SCHEME_NEGOTIATE }
850 static enum auth_scheme scheme_from_flag( DWORD flag )
852 int i;
854 for (i = 0; i < ARRAY_SIZE( auth_schemes ); i++) if (flag == auth_schemes[i].scheme) return i;
855 return SCHEME_INVALID;
858 static DWORD auth_scheme_from_header( const WCHAR *header )
860 unsigned int i;
862 for (i = 0; i < ARRAY_SIZE( auth_schemes ); i++)
864 if (!wcsnicmp( header, auth_schemes[i].str, auth_schemes[i].len ) &&
865 (header[auth_schemes[i].len] == ' ' || !header[auth_schemes[i].len])) return auth_schemes[i].scheme;
867 return 0;
870 static DWORD query_auth_schemes( struct request *request, DWORD level, DWORD *supported, DWORD *first )
872 DWORD ret, index = 0, supported_schemes = 0, first_scheme = 0;
874 for (;;)
876 WCHAR *buffer;
877 DWORD size, scheme;
879 size = 0;
880 ret = query_headers( request, level, NULL, NULL, &size, &index );
881 if (ret != ERROR_INSUFFICIENT_BUFFER)
883 if (index) ret = ERROR_SUCCESS;
884 break;
887 if (!(buffer = malloc( size ))) return ERROR_OUTOFMEMORY;
888 if ((ret = query_headers( request, level, NULL, buffer, &size, &index )))
890 free( buffer );
891 return ret;
893 scheme = auth_scheme_from_header( buffer );
894 free( buffer );
895 if (!scheme) continue;
897 if (!first_scheme) first_scheme = scheme;
898 supported_schemes |= scheme;
901 if (!ret)
903 *supported = supported_schemes;
904 *first = first_scheme;
906 return ret;
909 /***********************************************************************
910 * WinHttpQueryAuthSchemes (winhttp.@)
912 BOOL WINAPI WinHttpQueryAuthSchemes( HINTERNET hrequest, LPDWORD supported, LPDWORD first, LPDWORD target )
914 DWORD ret;
915 struct request *request;
917 TRACE("%p, %p, %p, %p\n", hrequest, supported, first, target);
919 if (!(request = (struct request *)grab_object( hrequest )))
921 SetLastError( ERROR_INVALID_HANDLE );
922 return FALSE;
924 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
926 release_object( &request->hdr );
927 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
928 return FALSE;
930 if (!supported || !first || !target)
932 release_object( &request->hdr );
933 SetLastError( ERROR_INVALID_PARAMETER );
934 return FALSE;
938 if (!(ret = query_auth_schemes( request, WINHTTP_QUERY_WWW_AUTHENTICATE, supported, first )))
940 *target = WINHTTP_AUTH_TARGET_SERVER;
942 else if (!(ret = query_auth_schemes( request, WINHTTP_QUERY_PROXY_AUTHENTICATE, supported, first )))
944 *target = WINHTTP_AUTH_TARGET_PROXY;
946 else ret = ERROR_INVALID_OPERATION;
948 release_object( &request->hdr );
949 SetLastError( ret );
950 return !ret;
953 static UINT encode_base64( const char *bin, unsigned int len, WCHAR *base64 )
955 UINT n = 0, x;
956 static const char base64enc[] =
957 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
959 while (len > 0)
961 /* first 6 bits, all from bin[0] */
962 base64[n++] = base64enc[(bin[0] & 0xfc) >> 2];
963 x = (bin[0] & 3) << 4;
965 /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
966 if (len == 1)
968 base64[n++] = base64enc[x];
969 base64[n++] = '=';
970 base64[n++] = '=';
971 break;
973 base64[n++] = base64enc[x | ((bin[1] & 0xf0) >> 4)];
974 x = (bin[1] & 0x0f) << 2;
976 /* next 6 bits 4 from bin[1] and 2 from bin[2] */
977 if (len == 2)
979 base64[n++] = base64enc[x];
980 base64[n++] = '=';
981 break;
983 base64[n++] = base64enc[x | ((bin[2] & 0xc0) >> 6)];
985 /* last 6 bits, all from bin [2] */
986 base64[n++] = base64enc[bin[2] & 0x3f];
987 bin += 3;
988 len -= 3;
990 base64[n] = 0;
991 return n;
994 static inline char decode_char( WCHAR c )
996 if (c >= 'A' && c <= 'Z') return c - 'A';
997 if (c >= 'a' && c <= 'z') return c - 'a' + 26;
998 if (c >= '0' && c <= '9') return c - '0' + 52;
999 if (c == '+') return 62;
1000 if (c == '/') return 63;
1001 return 64;
1004 static unsigned int decode_base64( const WCHAR *base64, unsigned int len, char *buf )
1006 unsigned int i = 0;
1007 char c0, c1, c2, c3;
1008 const WCHAR *p = base64;
1010 while (len > 4)
1012 if ((c0 = decode_char( p[0] )) > 63) return 0;
1013 if ((c1 = decode_char( p[1] )) > 63) return 0;
1014 if ((c2 = decode_char( p[2] )) > 63) return 0;
1015 if ((c3 = decode_char( p[3] )) > 63) return 0;
1017 if (buf)
1019 buf[i + 0] = (c0 << 2) | (c1 >> 4);
1020 buf[i + 1] = (c1 << 4) | (c2 >> 2);
1021 buf[i + 2] = (c2 << 6) | c3;
1023 len -= 4;
1024 i += 3;
1025 p += 4;
1027 if (p[2] == '=')
1029 if ((c0 = decode_char( p[0] )) > 63) return 0;
1030 if ((c1 = decode_char( p[1] )) > 63) return 0;
1032 if (buf) buf[i] = (c0 << 2) | (c1 >> 4);
1033 i++;
1035 else if (p[3] == '=')
1037 if ((c0 = decode_char( p[0] )) > 63) return 0;
1038 if ((c1 = decode_char( p[1] )) > 63) return 0;
1039 if ((c2 = decode_char( p[2] )) > 63) return 0;
1041 if (buf)
1043 buf[i + 0] = (c0 << 2) | (c1 >> 4);
1044 buf[i + 1] = (c1 << 4) | (c2 >> 2);
1046 i += 2;
1048 else
1050 if ((c0 = decode_char( p[0] )) > 63) return 0;
1051 if ((c1 = decode_char( p[1] )) > 63) return 0;
1052 if ((c2 = decode_char( p[2] )) > 63) return 0;
1053 if ((c3 = decode_char( p[3] )) > 63) return 0;
1055 if (buf)
1057 buf[i + 0] = (c0 << 2) | (c1 >> 4);
1058 buf[i + 1] = (c1 << 4) | (c2 >> 2);
1059 buf[i + 2] = (c2 << 6) | c3;
1061 i += 3;
1063 return i;
1066 static struct authinfo *alloc_authinfo(void)
1068 struct authinfo *ret;
1070 if (!(ret = malloc( sizeof(*ret) ))) return NULL;
1072 SecInvalidateHandle( &ret->cred );
1073 SecInvalidateHandle( &ret->ctx );
1074 memset( &ret->exp, 0, sizeof(ret->exp) );
1075 ret->scheme = 0;
1076 ret->attr = 0;
1077 ret->max_token = 0;
1078 ret->data = NULL;
1079 ret->data_len = 0;
1080 ret->finished = FALSE;
1081 return ret;
1084 void destroy_authinfo( struct authinfo *authinfo )
1086 if (!authinfo) return;
1088 if (SecIsValidHandle( &authinfo->ctx ))
1089 DeleteSecurityContext( &authinfo->ctx );
1090 if (SecIsValidHandle( &authinfo->cred ))
1091 FreeCredentialsHandle( &authinfo->cred );
1093 free( authinfo->data );
1094 free( authinfo );
1097 static BOOL get_authvalue( struct request *request, DWORD level, DWORD scheme, WCHAR *buffer, DWORD len )
1099 DWORD size, index = 0;
1100 for (;;)
1102 size = len;
1103 if (query_headers( request, level, NULL, buffer, &size, &index )) return FALSE;
1104 if (auth_scheme_from_header( buffer ) == scheme) break;
1106 return TRUE;
1109 static BOOL do_authorization( struct request *request, DWORD target, DWORD scheme_flag )
1111 struct authinfo *authinfo, **auth_ptr;
1112 enum auth_scheme scheme = scheme_from_flag( scheme_flag );
1113 const WCHAR *auth_target, *username, *password;
1114 WCHAR auth_value[2048], *auth_reply;
1115 DWORD len = sizeof(auth_value), len_scheme, flags;
1116 BOOL ret, has_auth_value;
1118 if (scheme == SCHEME_INVALID) return FALSE;
1120 switch (target)
1122 case WINHTTP_AUTH_TARGET_SERVER:
1123 has_auth_value = get_authvalue( request, WINHTTP_QUERY_WWW_AUTHENTICATE, scheme_flag, auth_value, len );
1124 auth_ptr = &request->authinfo;
1125 auth_target = L"Authorization";
1126 if (request->creds[TARGET_SERVER][scheme].username)
1128 if (scheme != SCHEME_BASIC && !has_auth_value) return FALSE;
1129 username = request->creds[TARGET_SERVER][scheme].username;
1130 password = request->creds[TARGET_SERVER][scheme].password;
1132 else
1134 if (!has_auth_value) return FALSE;
1135 username = request->connect->username;
1136 password = request->connect->password;
1138 break;
1140 case WINHTTP_AUTH_TARGET_PROXY:
1141 if (!get_authvalue( request, WINHTTP_QUERY_PROXY_AUTHENTICATE, scheme_flag, auth_value, len ))
1142 return FALSE;
1143 auth_ptr = &request->proxy_authinfo;
1144 auth_target = L"Proxy-Authorization";
1145 if (request->creds[TARGET_PROXY][scheme].username)
1147 username = request->creds[TARGET_PROXY][scheme].username;
1148 password = request->creds[TARGET_PROXY][scheme].password;
1150 else
1152 username = request->connect->session->proxy_username;
1153 password = request->connect->session->proxy_password;
1155 break;
1157 default:
1158 WARN( "unknown target %#lx\n", target );
1159 return FALSE;
1161 authinfo = *auth_ptr;
1163 switch (scheme)
1165 case SCHEME_BASIC:
1167 int userlen, passlen;
1169 if (!username || !password) return FALSE;
1170 if ((!authinfo && !(authinfo = alloc_authinfo())) || authinfo->finished) return FALSE;
1172 userlen = WideCharToMultiByte( CP_UTF8, 0, username, lstrlenW( username ), NULL, 0, NULL, NULL );
1173 passlen = WideCharToMultiByte( CP_UTF8, 0, password, lstrlenW( password ), NULL, 0, NULL, NULL );
1175 authinfo->data_len = userlen + 1 + passlen;
1176 if (!(authinfo->data = malloc( authinfo->data_len ))) return FALSE;
1178 WideCharToMultiByte( CP_UTF8, 0, username, -1, authinfo->data, userlen, NULL, NULL );
1179 authinfo->data[userlen] = ':';
1180 WideCharToMultiByte( CP_UTF8, 0, password, -1, authinfo->data + userlen + 1, passlen, NULL, NULL );
1182 authinfo->scheme = SCHEME_BASIC;
1183 authinfo->finished = TRUE;
1184 break;
1186 case SCHEME_NTLM:
1187 case SCHEME_NEGOTIATE:
1189 SECURITY_STATUS status;
1190 SecBufferDesc out_desc, in_desc;
1191 SecBuffer out, in;
1192 ULONG flags = ISC_REQ_CONNECTION|ISC_REQ_USE_DCE_STYLE|ISC_REQ_MUTUAL_AUTH|ISC_REQ_DELEGATE;
1193 const WCHAR *p;
1194 BOOL first = FALSE;
1196 if (!authinfo)
1198 TimeStamp exp;
1199 SEC_WINNT_AUTH_IDENTITY_W id;
1200 WCHAR *domain, *user;
1202 if (!username || !password || !(authinfo = alloc_authinfo())) return FALSE;
1204 first = TRUE;
1205 domain = (WCHAR *)username;
1206 user = wcschr( username, '\\' );
1208 if (user) user++;
1209 else
1211 user = (WCHAR *)username;
1212 domain = NULL;
1214 id.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1215 id.User = user;
1216 id.UserLength = lstrlenW( user );
1217 id.Domain = domain;
1218 id.DomainLength = domain ? user - domain - 1 : 0;
1219 id.Password = (WCHAR *)password;
1220 id.PasswordLength = lstrlenW( password );
1222 status = AcquireCredentialsHandleW( NULL, (SEC_WCHAR *)auth_schemes[scheme].str,
1223 SECPKG_CRED_OUTBOUND, NULL, &id, NULL, NULL,
1224 &authinfo->cred, &exp );
1225 if (status == SEC_E_OK)
1227 PSecPkgInfoW info;
1228 status = QuerySecurityPackageInfoW( (SEC_WCHAR *)auth_schemes[scheme].str, &info );
1229 if (status == SEC_E_OK)
1231 authinfo->max_token = info->cbMaxToken;
1232 FreeContextBuffer( info );
1235 if (status != SEC_E_OK)
1237 WARN( "AcquireCredentialsHandleW for scheme %s failed with error %#lx\n",
1238 debugstr_w(auth_schemes[scheme].str), status );
1239 free( authinfo );
1240 return FALSE;
1242 authinfo->scheme = scheme;
1244 else if (authinfo->finished) return FALSE;
1246 if ((lstrlenW( auth_value ) < auth_schemes[authinfo->scheme].len ||
1247 wcsnicmp( auth_value, auth_schemes[authinfo->scheme].str, auth_schemes[authinfo->scheme].len )))
1249 ERR("authentication scheme changed from %s to %s\n",
1250 debugstr_w(auth_schemes[authinfo->scheme].str), debugstr_w(auth_value));
1251 destroy_authinfo( authinfo );
1252 *auth_ptr = NULL;
1253 return FALSE;
1255 in.BufferType = SECBUFFER_TOKEN;
1256 in.cbBuffer = 0;
1257 in.pvBuffer = NULL;
1259 in_desc.ulVersion = 0;
1260 in_desc.cBuffers = 1;
1261 in_desc.pBuffers = &in;
1263 p = auth_value + auth_schemes[scheme].len;
1264 if (*p == ' ')
1266 int len = lstrlenW( ++p );
1267 in.cbBuffer = decode_base64( p, len, NULL );
1268 if (!(in.pvBuffer = malloc( in.cbBuffer ))) {
1269 destroy_authinfo( authinfo );
1270 *auth_ptr = NULL;
1271 return FALSE;
1273 decode_base64( p, len, in.pvBuffer );
1275 out.BufferType = SECBUFFER_TOKEN;
1276 out.cbBuffer = authinfo->max_token;
1277 if (!(out.pvBuffer = malloc( authinfo->max_token )))
1279 free( in.pvBuffer );
1280 destroy_authinfo( authinfo );
1281 *auth_ptr = NULL;
1282 return FALSE;
1284 out_desc.ulVersion = 0;
1285 out_desc.cBuffers = 1;
1286 out_desc.pBuffers = &out;
1288 status = InitializeSecurityContextW( first ? &authinfo->cred : NULL, first ? NULL : &authinfo->ctx,
1289 first ? request->connect->servername : NULL, flags, 0,
1290 SECURITY_NETWORK_DREP, in.pvBuffer ? &in_desc : NULL, 0,
1291 &authinfo->ctx, &out_desc, &authinfo->attr, &authinfo->exp );
1292 free( in.pvBuffer );
1293 if (status == SEC_E_OK)
1295 free( authinfo->data );
1296 authinfo->data = out.pvBuffer;
1297 authinfo->data_len = out.cbBuffer;
1298 authinfo->finished = TRUE;
1299 TRACE("sending last auth packet\n");
1301 else if (status == SEC_I_CONTINUE_NEEDED)
1303 free( authinfo->data );
1304 authinfo->data = out.pvBuffer;
1305 authinfo->data_len = out.cbBuffer;
1306 TRACE("sending next auth packet\n");
1308 else
1310 ERR( "InitializeSecurityContextW failed with error %#lx\n", status );
1311 free( out.pvBuffer );
1312 destroy_authinfo( authinfo );
1313 *auth_ptr = NULL;
1314 return FALSE;
1316 break;
1318 default:
1319 ERR("invalid scheme %u\n", scheme);
1320 return FALSE;
1322 *auth_ptr = authinfo;
1324 len_scheme = auth_schemes[authinfo->scheme].len;
1325 len = len_scheme + 1 + ((authinfo->data_len + 2) * 4) / 3;
1326 if (!(auth_reply = malloc( (len + 1) * sizeof(WCHAR) ))) return FALSE;
1328 memcpy( auth_reply, auth_schemes[authinfo->scheme].str, len_scheme * sizeof(WCHAR) );
1329 auth_reply[len_scheme] = ' ';
1330 encode_base64( authinfo->data, authinfo->data_len, auth_reply + len_scheme + 1 );
1332 flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE;
1333 ret = !process_header( request, auth_target, auth_reply, flags, TRUE );
1334 free( auth_reply );
1335 return ret;
1338 static WCHAR *build_proxy_connect_string( struct request *request )
1340 WCHAR *ret, *host;
1341 unsigned int i;
1342 int len = lstrlenW( request->connect->hostname ) + 7;
1344 if (!(host = malloc( len * sizeof(WCHAR) ))) return NULL;
1345 len = swprintf( host, len, L"%s:%u", request->connect->hostname, request->connect->hostport );
1347 len += ARRAY_SIZE(L"CONNECT");
1348 len += ARRAY_SIZE(L"HTTP/1.1");
1350 for (i = 0; i < request->num_headers; i++)
1352 if (request->headers[i].is_request)
1353 len += lstrlenW( request->headers[i].field ) + lstrlenW( request->headers[i].value ) + 4; /* '\r\n: ' */
1355 len += 4; /* '\r\n\r\n' */
1357 if ((ret = malloc( (len + 1) * sizeof(WCHAR) )))
1359 lstrcpyW( ret, L"CONNECT" );
1360 lstrcatW( ret, L" " );
1361 lstrcatW( ret, host );
1362 lstrcatW( ret, L" " );
1363 lstrcatW( ret, L"HTTP/1.1" );
1365 for (i = 0; i < request->num_headers; i++)
1367 if (request->headers[i].is_request)
1369 lstrcatW( ret, L"\r\n" );
1370 lstrcatW( ret, request->headers[i].field );
1371 lstrcatW( ret, L": " );
1372 lstrcatW( ret, request->headers[i].value );
1375 lstrcatW( ret, L"\r\n\r\n" );
1378 free( host );
1379 return ret;
1382 static DWORD read_reply( struct request *request );
1384 static DWORD secure_proxy_connect( struct request *request )
1386 WCHAR *str;
1387 char *strA;
1388 int len, bytes_sent;
1389 DWORD ret;
1391 if (!(str = build_proxy_connect_string( request ))) return ERROR_OUTOFMEMORY;
1392 strA = strdupWA( str );
1393 free( str );
1394 if (!strA) return ERROR_OUTOFMEMORY;
1396 len = strlen( strA );
1397 ret = netconn_send( request->netconn, strA, len, &bytes_sent, NULL );
1398 free( strA );
1399 if (!ret) ret = read_reply( request );
1401 return ret;
1404 static WCHAR *addr_to_str( struct sockaddr_storage *addr )
1406 char buf[INET6_ADDRSTRLEN];
1407 void *src;
1409 switch (addr->ss_family)
1411 case AF_INET:
1412 src = &((struct sockaddr_in *)addr)->sin_addr;
1413 break;
1414 case AF_INET6:
1415 src = &((struct sockaddr_in6 *)addr)->sin6_addr;
1416 break;
1417 default:
1418 WARN("unsupported address family %d\n", addr->ss_family);
1419 return NULL;
1421 if (!inet_ntop( addr->ss_family, src, buf, sizeof(buf) )) return NULL;
1422 return strdupAW( buf );
1425 static CRITICAL_SECTION connection_pool_cs;
1426 static CRITICAL_SECTION_DEBUG connection_pool_debug =
1428 0, 0, &connection_pool_cs,
1429 { &connection_pool_debug.ProcessLocksList, &connection_pool_debug.ProcessLocksList },
1430 0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
1432 static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
1434 static struct list connection_pool = LIST_INIT( connection_pool );
1436 void release_host( struct hostdata *host )
1438 LONG ref;
1440 EnterCriticalSection( &connection_pool_cs );
1441 if (!(ref = --host->ref)) list_remove( &host->entry );
1442 LeaveCriticalSection( &connection_pool_cs );
1443 if (ref) return;
1445 assert( list_empty( &host->connections ) );
1446 free( host->hostname );
1447 free( host );
1450 static BOOL connection_collector_running;
1452 static void CALLBACK connection_collector( TP_CALLBACK_INSTANCE *instance, void *ctx )
1454 unsigned int remaining_connections;
1455 struct netconn *netconn, *next_netconn;
1456 struct hostdata *host, *next_host;
1457 ULONGLONG now;
1461 /* FIXME: Use more sophisticated method */
1462 Sleep(5000);
1463 remaining_connections = 0;
1464 now = GetTickCount64();
1466 EnterCriticalSection(&connection_pool_cs);
1468 LIST_FOR_EACH_ENTRY_SAFE(host, next_host, &connection_pool, struct hostdata, entry)
1470 LIST_FOR_EACH_ENTRY_SAFE(netconn, next_netconn, &host->connections, struct netconn, entry)
1472 if (netconn->keep_until < now)
1474 TRACE("freeing %p\n", netconn);
1475 list_remove(&netconn->entry);
1476 netconn_release(netconn);
1478 else remaining_connections++;
1482 if (!remaining_connections) connection_collector_running = FALSE;
1484 LeaveCriticalSection(&connection_pool_cs);
1485 } while(remaining_connections);
1487 FreeLibraryWhenCallbackReturns( instance, winhttp_instance );
1490 static void cache_connection( struct netconn *netconn )
1492 TRACE( "caching connection %p\n", netconn );
1494 EnterCriticalSection( &connection_pool_cs );
1496 netconn->keep_until = GetTickCount64() + DEFAULT_KEEP_ALIVE_TIMEOUT;
1497 list_add_head( &netconn->host->connections, &netconn->entry );
1499 if (!connection_collector_running)
1501 HMODULE module;
1503 GetModuleHandleExW( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR *)winhttp_instance, &module );
1505 if (TrySubmitThreadpoolCallback( connection_collector, NULL, NULL )) connection_collector_running = TRUE;
1506 else FreeLibrary( winhttp_instance );
1509 LeaveCriticalSection( &connection_pool_cs );
1512 static DWORD map_secure_protocols( DWORD mask )
1514 DWORD ret = 0;
1515 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_SSL2) ret |= SP_PROT_SSL2_CLIENT;
1516 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_SSL3) ret |= SP_PROT_SSL3_CLIENT;
1517 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1) ret |= SP_PROT_TLS1_CLIENT;
1518 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1) ret |= SP_PROT_TLS1_1_CLIENT;
1519 if (mask & WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2) ret |= SP_PROT_TLS1_2_CLIENT;
1520 return ret;
1523 static DWORD ensure_cred_handle( struct request *request )
1525 SECURITY_STATUS status = SEC_E_OK;
1527 if (request->cred_handle_initialized) return ERROR_SUCCESS;
1529 if (!request->cred_handle_initialized)
1531 SCHANNEL_CRED cred;
1532 memset( &cred, 0, sizeof(cred) );
1533 cred.dwVersion = SCHANNEL_CRED_VERSION;
1534 cred.grbitEnabledProtocols = map_secure_protocols( request->connect->session->secure_protocols );
1535 if (request->client_cert)
1537 cred.paCred = &request->client_cert;
1538 cred.cCreds = 1;
1540 status = AcquireCredentialsHandleW( NULL, (WCHAR *)UNISP_NAME_W, SECPKG_CRED_OUTBOUND, NULL,
1541 &cred, NULL, NULL, &request->cred_handle, NULL );
1542 if (status == SEC_E_OK)
1543 request->cred_handle_initialized = TRUE;
1546 if (status != SEC_E_OK)
1548 WARN( "AcquireCredentialsHandleW failed: %#lx\n", status );
1549 return status;
1551 return ERROR_SUCCESS;
1554 static DWORD open_connection( struct request *request )
1556 BOOL is_secure = request->hdr.flags & WINHTTP_FLAG_SECURE;
1557 struct hostdata *host = NULL, *iter;
1558 struct netconn *netconn = NULL;
1559 struct connect *connect;
1560 WCHAR *addressW = NULL;
1561 INTERNET_PORT port;
1562 DWORD ret, len;
1564 if (request->netconn) goto done;
1566 connect = request->connect;
1567 port = connect->serverport ? connect->serverport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80);
1569 EnterCriticalSection( &connection_pool_cs );
1571 LIST_FOR_EACH_ENTRY( iter, &connection_pool, struct hostdata, entry )
1573 if (iter->port == port && !wcscmp( connect->servername, iter->hostname ) && !is_secure == !iter->secure)
1575 host = iter;
1576 host->ref++;
1577 break;
1581 if (!host)
1583 if ((host = malloc( sizeof(*host) )))
1585 host->ref = 1;
1586 host->secure = is_secure;
1587 host->port = port;
1588 list_init( &host->connections );
1589 if ((host->hostname = wcsdup( connect->servername )))
1591 list_add_head( &connection_pool, &host->entry );
1593 else
1595 free( host );
1596 host = NULL;
1601 LeaveCriticalSection( &connection_pool_cs );
1603 if (!host) return ERROR_OUTOFMEMORY;
1605 for (;;)
1607 EnterCriticalSection( &connection_pool_cs );
1608 if (!list_empty( &host->connections ))
1610 netconn = LIST_ENTRY( list_head( &host->connections ), struct netconn, entry );
1611 list_remove( &netconn->entry );
1613 LeaveCriticalSection( &connection_pool_cs );
1614 if (!netconn) break;
1616 if (netconn_is_alive( netconn )) break;
1617 TRACE("connection %p no longer alive, closing\n", netconn);
1618 netconn_release( netconn );
1619 netconn = NULL;
1622 if (!connect->resolved && netconn)
1624 connect->sockaddr = netconn->sockaddr;
1625 connect->resolved = TRUE;
1628 if (!connect->resolved)
1630 len = lstrlenW( host->hostname ) + 1;
1631 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, host->hostname, len );
1633 if ((ret = netconn_resolve( host->hostname, port, &connect->sockaddr, request->resolve_timeout )))
1635 release_host( host );
1636 return ret;
1638 connect->resolved = TRUE;
1640 if (!(addressW = addr_to_str( &connect->sockaddr )))
1642 release_host( host );
1643 return ERROR_OUTOFMEMORY;
1645 len = lstrlenW( addressW ) + 1;
1646 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, addressW, len );
1649 if (!netconn)
1651 if (!addressW && !(addressW = addr_to_str( &connect->sockaddr )))
1653 release_host( host );
1654 return ERROR_OUTOFMEMORY;
1657 TRACE("connecting to %s:%u\n", debugstr_w(addressW), port);
1659 len = lstrlenW( addressW ) + 1;
1660 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, addressW, len );
1662 if ((ret = netconn_create( host, &connect->sockaddr, request->connect_timeout, &netconn )))
1664 free( addressW );
1665 release_host( host );
1666 return ret;
1668 netconn_set_timeout( netconn, TRUE, request->send_timeout );
1669 netconn_set_timeout( netconn, FALSE, request_receive_response_timeout( request ));
1671 request->netconn = netconn;
1673 if (is_secure)
1675 if (connect->session->proxy_server && wcsicmp( connect->hostname, connect->servername ))
1677 if ((ret = secure_proxy_connect( request )))
1679 request->netconn = NULL;
1680 free( addressW );
1681 netconn_release( netconn );
1682 return ret;
1686 CertFreeCertificateContext( request->server_cert );
1687 request->server_cert = NULL;
1689 if ((ret = ensure_cred_handle( request )) ||
1690 (ret = netconn_secure_connect( netconn, connect->hostname, request->security_flags,
1691 &request->cred_handle, request->check_revocation )))
1693 request->netconn = NULL;
1694 free( addressW );
1695 netconn_release( netconn );
1696 return ret;
1700 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, addressW, lstrlenW(addressW) + 1 );
1702 else
1704 TRACE("using connection %p\n", netconn);
1706 netconn_set_timeout( netconn, TRUE, request->send_timeout );
1707 netconn_set_timeout( netconn, FALSE, request_receive_response_timeout( request ));
1708 request->netconn = netconn;
1711 if (netconn->secure && !(request->server_cert = netconn_get_certificate( netconn )))
1713 free( addressW );
1714 netconn_release( netconn );
1715 return ERROR_WINHTTP_SECURE_FAILURE;
1718 done:
1719 request->read_pos = request->read_size = 0;
1720 request->read_chunked = FALSE;
1721 request->read_chunked_size = ~0u;
1722 request->read_chunked_eof = FALSE;
1723 free( addressW );
1724 return ERROR_SUCCESS;
1727 void close_connection( struct request *request )
1729 if (!request->netconn) return;
1731 netconn_release( request->netconn );
1732 request->netconn = NULL;
1735 static DWORD add_host_header( struct request *request, DWORD modifier )
1737 DWORD ret, len;
1738 WCHAR *host;
1739 struct connect *connect = request->connect;
1740 INTERNET_PORT port;
1742 port = connect->hostport ? connect->hostport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80);
1744 if (port == INTERNET_DEFAULT_HTTP_PORT || port == INTERNET_DEFAULT_HTTPS_PORT)
1746 return process_header( request, L"Host", connect->hostname, modifier, TRUE );
1748 len = lstrlenW( connect->hostname ) + 7; /* sizeof(":65335") */
1749 if (!(host = malloc( len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
1750 swprintf( host, len, L"%s:%u", connect->hostname, port );
1751 ret = process_header( request, L"Host", host, modifier, TRUE );
1752 free( host );
1753 return ret;
1756 static void clear_response_headers( struct request *request )
1758 unsigned int i;
1760 for (i = 0; i < request->num_headers; i++)
1762 if (!request->headers[i].field) continue;
1763 if (!request->headers[i].value) continue;
1764 if (request->headers[i].is_request) continue;
1765 delete_header( request, i );
1766 i--;
1770 /* remove some amount of data from the read buffer */
1771 static void remove_data( struct request *request, int count )
1773 if (!(request->read_size -= count)) request->read_pos = 0;
1774 else request->read_pos += count;
1777 /* read some more data into the read buffer */
1778 static DWORD read_more_data( struct request *request, int maxlen, BOOL notify )
1780 int len;
1781 DWORD ret;
1783 if (request->read_chunked_eof) return ERROR_INSUFFICIENT_BUFFER;
1785 if (request->read_size && request->read_pos)
1787 /* move existing data to the start of the buffer */
1788 memmove( request->read_buf, request->read_buf + request->read_pos, request->read_size );
1789 request->read_pos = 0;
1791 if (maxlen == -1) maxlen = sizeof(request->read_buf);
1793 if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 );
1795 ret = netconn_recv( request->netconn, request->read_buf + request->read_size,
1796 maxlen - request->read_size, 0, &len );
1798 if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, &len, sizeof(len) );
1799 request->read_reply_len += len;
1801 request->read_size += len;
1802 return ret;
1805 /* discard data contents until we reach end of line */
1806 static DWORD discard_eol( struct request *request, BOOL notify )
1808 DWORD ret;
1811 char *eol = memchr( request->read_buf + request->read_pos, '\n', request->read_size );
1812 if (eol)
1814 remove_data( request, (eol + 1) - (request->read_buf + request->read_pos) );
1815 break;
1817 request->read_pos = request->read_size = 0; /* discard everything */
1818 if ((ret = read_more_data( request, -1, notify ))) return ret;
1819 } while (request->read_size);
1820 return ERROR_SUCCESS;
1823 static void update_value_from_digit( DWORD *value, char ch )
1825 if (ch >= '0' && ch <= '9') *value = *value * 16 + ch - '0';
1826 else if (ch >= 'a' && ch <= 'f') *value = *value * 16 + ch - 'a' + 10;
1827 else if (ch >= 'A' && ch <= 'F') *value = *value * 16 + ch - 'A' + 10;
1830 /* read chunk size if already in the read buffer */
1831 static BOOL get_chunk_size( struct request *request )
1833 DWORD chunk_size;
1834 char *p, *eol;
1836 if (request->read_chunked_size != ~0ul) return TRUE;
1838 eol = memchr( request->read_buf + request->read_pos, '\n', request->read_size );
1839 if (!eol) return FALSE;
1841 chunk_size = 0;
1842 for (p = request->read_buf + request->read_pos; p != eol; ++p)
1844 if (*p == ';' || *p == '\r') break;
1845 update_value_from_digit( &chunk_size, *p );
1848 request->read_chunked_size = chunk_size;
1849 if (!chunk_size) request->read_chunked_eof = TRUE;
1851 remove_data( request, (eol + 1) - (request->read_buf + request->read_pos) );
1852 return TRUE;
1855 /* read the size of the next chunk */
1856 static DWORD start_next_chunk( struct request *request, BOOL notify )
1858 DWORD ret, chunk_size = 0;
1860 assert(!request->read_chunked_size || request->read_chunked_size == ~0u);
1862 if (request->read_chunked_eof) return ERROR_INSUFFICIENT_BUFFER;
1864 /* read terminator for the previous chunk */
1865 if (!request->read_chunked_size && (ret = discard_eol( request, notify ))) return ret;
1867 for (;;)
1869 while (request->read_size)
1871 char ch = request->read_buf[request->read_pos];
1873 if (ch == ';' || ch == '\r' || ch == '\n')
1875 TRACE( "reading %lu byte chunk\n", chunk_size );
1877 if (request->content_length == ~0u) request->content_length = chunk_size;
1878 else request->content_length += chunk_size;
1880 request->read_chunked_size = chunk_size;
1881 if (!chunk_size) request->read_chunked_eof = TRUE;
1883 return discard_eol( request, notify );
1885 update_value_from_digit( &chunk_size, ch );
1886 remove_data( request, 1 );
1888 if ((ret = read_more_data( request, -1, notify ))) return ret;
1889 if (!request->read_size)
1891 request->content_length = request->content_read = 0;
1892 request->read_chunked_size = 0;
1893 return ERROR_SUCCESS;
1898 static DWORD refill_buffer( struct request *request, BOOL notify )
1900 int len = sizeof(request->read_buf);
1901 DWORD ret;
1903 if (request->read_chunked)
1905 if (request->read_chunked_eof) return ERROR_INSUFFICIENT_BUFFER;
1906 if (request->read_chunked_size == ~0u || !request->read_chunked_size)
1908 if ((ret = start_next_chunk( request, notify ))) return ret;
1910 len = min( len, request->read_chunked_size );
1912 else if (request->content_length != ~0u)
1914 len = min( len, request->content_length - request->content_read );
1917 if (len <= request->read_size) return ERROR_SUCCESS;
1918 if ((ret = read_more_data( request, len, notify ))) return ret;
1919 if (!request->read_size) request->content_length = request->content_read = 0;
1920 return ERROR_SUCCESS;
1923 static void finished_reading( struct request *request )
1925 BOOL close = FALSE, close_request_headers;
1926 WCHAR connection[20];
1927 DWORD size = sizeof(connection);
1929 if (!request->netconn) return;
1931 if (request->netconn->socket == -1) close = TRUE;
1932 else if (request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE) close = TRUE;
1933 else if (!query_headers( request, WINHTTP_QUERY_CONNECTION, NULL, connection, &size, NULL ) ||
1934 !query_headers( request, WINHTTP_QUERY_PROXY_CONNECTION, NULL, connection, &size, NULL ))
1936 if (!wcsicmp( connection, L"close" )) close = TRUE;
1938 else if (!wcscmp( request->version, L"HTTP/1.0" )) close = TRUE;
1940 size = sizeof(connection);
1941 close_request_headers =
1942 (!query_headers( request, WINHTTP_QUERY_CONNECTION | WINHTTP_QUERY_FLAG_REQUEST_HEADERS, NULL, connection, &size, NULL )
1943 || !query_headers( request, WINHTTP_QUERY_PROXY_CONNECTION | WINHTTP_QUERY_FLAG_REQUEST_HEADERS, NULL, connection, &size, NULL ))
1944 && !wcsicmp( connection, L"close" );
1945 if (close || close_request_headers)
1947 if (close_request_headers) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, 0, 0 );
1948 netconn_release( request->netconn );
1949 if (close_request_headers) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, 0, 0 );
1951 else
1952 cache_connection( request->netconn );
1953 request->netconn = NULL;
1956 /* return the size of data available to be read immediately */
1957 static DWORD get_available_data( struct request *request )
1959 if (request->read_chunked)
1961 if (!get_chunk_size( request )) return 0;
1962 return min( request->read_chunked_size, request->read_size );
1964 return request->read_size;
1967 /* check if we have reached the end of the data to read */
1968 static BOOL end_of_read_data( struct request *request )
1970 if (!request->content_length) return TRUE;
1971 if (request->read_chunked) return request->read_chunked_eof;
1972 if (request->content_length == ~0u) return FALSE;
1973 return (request->content_length == request->content_read);
1976 static DWORD read_data( struct request *request, void *buffer, DWORD size, DWORD *read, BOOL async )
1978 int count, bytes_read = 0;
1979 DWORD ret = ERROR_SUCCESS;
1981 if (request->read_chunked && request->read_chunked_size == ~0u
1982 && (ret = start_next_chunk( request, async ))) goto done;
1984 if (end_of_read_data( request )) goto done;
1986 while (size)
1988 if (!(count = get_available_data( request )))
1990 if ((ret = refill_buffer( request, async ))) goto done;
1991 if (!(count = get_available_data( request ))) goto done;
1993 count = min( count, size );
1994 memcpy( (char *)buffer + bytes_read, request->read_buf + request->read_pos, count );
1995 remove_data( request, count );
1996 if (request->read_chunked) request->read_chunked_size -= count;
1997 size -= count;
1998 bytes_read += count;
1999 request->content_read += count;
2000 if (end_of_read_data( request )) goto done;
2002 if (request->read_chunked && !request->read_chunked_size) ret = refill_buffer( request, async );
2004 done:
2005 TRACE( "retrieved %u bytes (%lu/%lu)\n", bytes_read, request->content_read, request->content_length );
2006 if (end_of_read_data( request )) finished_reading( request );
2007 if (async)
2009 if (!ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, buffer, bytes_read );
2010 else
2012 WINHTTP_ASYNC_RESULT result;
2013 result.dwResult = API_READ_DATA;
2014 result.dwError = ret;
2015 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
2019 if (!ret && read) *read = bytes_read;
2020 return ret;
2023 /* read any content returned by the server so that the connection can be reused */
2024 static void drain_content( struct request *request )
2026 DWORD size, bytes_read, bytes_total = 0, bytes_left = request->content_length - request->content_read;
2027 char buffer[2048];
2029 refill_buffer( request, FALSE );
2030 for (;;)
2032 if (request->read_chunked) size = sizeof(buffer);
2033 else size = min( sizeof(buffer), bytes_left - bytes_total );
2035 if (read_data( request, buffer, size, &bytes_read, FALSE ) || !bytes_read) return;
2036 bytes_total += bytes_read;
2040 enum escape_flags
2042 ESCAPE_FLAG_NON_PRINTABLE = 0x01,
2043 ESCAPE_FLAG_SPACE = 0x02,
2044 ESCAPE_FLAG_PERCENT = 0x04,
2045 ESCAPE_FLAG_UNSAFE = 0x08,
2046 ESCAPE_FLAG_DEL = 0x10,
2047 ESCAPE_FLAG_8BIT = 0x20,
2048 ESCAPE_FLAG_STRIP_CRLF = 0x40,
2051 #define ESCAPE_MASK_DEFAULT (ESCAPE_FLAG_NON_PRINTABLE | ESCAPE_FLAG_SPACE | ESCAPE_FLAG_UNSAFE |\
2052 ESCAPE_FLAG_DEL | ESCAPE_FLAG_8BIT)
2053 #define ESCAPE_MASK_PERCENT (ESCAPE_FLAG_PERCENT | ESCAPE_MASK_DEFAULT)
2054 #define ESCAPE_MASK_DISABLE (ESCAPE_FLAG_SPACE | ESCAPE_FLAG_8BIT | ESCAPE_FLAG_STRIP_CRLF)
2056 static inline BOOL need_escape( char ch, enum escape_flags flags )
2058 static const char unsafe[] = "\"#<>[\\]^`{|}";
2059 const char *ptr = unsafe;
2061 if ((flags & ESCAPE_FLAG_SPACE) && ch == ' ') return TRUE;
2062 if ((flags & ESCAPE_FLAG_PERCENT) && ch == '%') return TRUE;
2063 if ((flags & ESCAPE_FLAG_NON_PRINTABLE) && ch < 0x20) return TRUE;
2064 if ((flags & ESCAPE_FLAG_DEL) && ch == 0x7f) return TRUE;
2065 if ((flags & ESCAPE_FLAG_8BIT) && (ch & 0x80)) return TRUE;
2066 if ((flags & ESCAPE_FLAG_UNSAFE)) while (*ptr) { if (ch == *ptr++) return TRUE; }
2067 return FALSE;
2070 static DWORD escape_string( const char *src, DWORD len, char *dst, enum escape_flags flags )
2072 static const char hex[] = "0123456789ABCDEF";
2073 DWORD i, ret = len;
2074 char *ptr = dst;
2076 for (i = 0; i < len; i++)
2078 if ((flags & ESCAPE_FLAG_STRIP_CRLF) && (src[i] == '\r' || src[i] == '\n'))
2080 ret--;
2081 continue;
2083 if (need_escape( src[i], flags ))
2085 if (dst)
2087 ptr[0] = '%';
2088 ptr[1] = hex[(src[i] >> 4) & 0xf];
2089 ptr[2] = hex[src[i] & 0xf];
2090 ptr += 3;
2092 ret += 2;
2094 else if (dst) *ptr++ = src[i];
2097 if (dst) dst[ret] = 0;
2098 return ret;
2101 static DWORD str_to_wire( const WCHAR *src, int src_len, char *dst, enum escape_flags flags )
2103 DWORD len;
2104 char *utf8;
2106 if (src_len < 0) src_len = lstrlenW( src );
2107 len = WideCharToMultiByte( CP_UTF8, 0, src, src_len, NULL, 0, NULL, NULL );
2108 if (!(utf8 = malloc( len ))) return 0;
2110 WideCharToMultiByte( CP_UTF8, 0, src, -1, utf8, len, NULL, NULL );
2111 len = escape_string( utf8, len, dst, flags );
2112 free( utf8 );
2114 return len;
2117 static char *build_wire_path( struct request *request, DWORD *ret_len )
2119 WCHAR *full_path;
2120 const WCHAR *start, *path, *query = NULL;
2121 DWORD len, len_path = 0, len_query = 0;
2122 enum escape_flags path_flags, query_flags;
2123 char *ret;
2125 if (!wcsicmp( request->connect->hostname, request->connect->servername )) start = full_path = request->path;
2126 else if (!(full_path = build_absolute_request_path( request, &start ))) return NULL;
2128 len = lstrlenW( full_path );
2129 if ((path = wcschr( start, '/' )))
2131 len_path = lstrlenW( path );
2132 if ((query = wcschr( path, '?' )))
2134 len_query = lstrlenW( query );
2135 len_path -= len_query;
2139 if (request->hdr.flags & WINHTTP_FLAG_ESCAPE_DISABLE) path_flags = ESCAPE_MASK_DISABLE;
2140 else if (request->hdr.flags & WINHTTP_FLAG_ESCAPE_PERCENT) path_flags = ESCAPE_MASK_PERCENT;
2141 else path_flags = ESCAPE_MASK_DEFAULT;
2143 if (request->hdr.flags & WINHTTP_FLAG_ESCAPE_DISABLE_QUERY) query_flags = ESCAPE_MASK_DISABLE;
2144 else query_flags = path_flags;
2146 *ret_len = str_to_wire( full_path, len - len_path - len_query, NULL, 0 );
2147 if (path) *ret_len += str_to_wire( path, len_path, NULL, path_flags );
2148 if (query) *ret_len += str_to_wire( query, len_query, NULL, query_flags );
2150 if ((ret = malloc( *ret_len + 1 )))
2152 len = str_to_wire( full_path, len - len_path - len_query, ret, 0 );
2153 if (path) len += str_to_wire( path, len_path, ret + len, path_flags );
2154 if (query) str_to_wire( query, len_query, ret + len, query_flags );
2157 if (full_path != request->path) free( full_path );
2158 return ret;
2161 static char *build_wire_request( struct request *request, DWORD *len )
2163 char *path, *ptr, *ret;
2164 DWORD i, len_path;
2166 if (!(path = build_wire_path( request, &len_path ))) return NULL;
2168 *len = str_to_wire( request->verb, -1, NULL, 0 ) + 1; /* ' ' */
2169 *len += len_path + 1; /* ' ' */
2170 *len += str_to_wire( request->version, -1, NULL, 0 );
2172 for (i = 0; i < request->num_headers; i++)
2174 if (request->headers[i].is_request)
2176 *len += str_to_wire( request->headers[i].field, -1, NULL, 0 ) + 2; /* ': ' */
2177 *len += str_to_wire( request->headers[i].value, -1, NULL, 0 ) + 2; /* '\r\n' */
2180 *len += 4; /* '\r\n\r\n' */
2182 if ((ret = ptr = malloc( *len + 1 )))
2184 ptr += str_to_wire( request->verb, -1, ptr, 0 );
2185 *ptr++ = ' ';
2186 memcpy( ptr, path, len_path );
2187 ptr += len_path;
2188 *ptr++ = ' ';
2189 ptr += str_to_wire( request->version, -1, ptr, 0 );
2191 for (i = 0; i < request->num_headers; i++)
2193 if (request->headers[i].is_request)
2195 *ptr++ = '\r';
2196 *ptr++ = '\n';
2197 ptr += str_to_wire( request->headers[i].field, -1, ptr, 0 );
2198 *ptr++ = ':';
2199 *ptr++ = ' ';
2200 ptr += str_to_wire( request->headers[i].value, -1, ptr, 0 );
2203 memcpy( ptr, "\r\n\r\n", sizeof("\r\n\r\n") );
2206 free( path );
2207 return ret;
2210 static WCHAR *create_websocket_key(void)
2212 WCHAR *ret;
2213 char buf[16];
2214 DWORD base64_len = ((sizeof(buf) + 2) * 4) / 3;
2215 if (!RtlGenRandom( buf, sizeof(buf) )) return NULL;
2216 if ((ret = malloc( (base64_len + 1) * sizeof(WCHAR) ))) encode_base64( buf, sizeof(buf), ret );
2217 return ret;
2220 static DWORD add_websocket_key_header( struct request *request )
2222 WCHAR *key = create_websocket_key();
2223 if (!key) return ERROR_OUTOFMEMORY;
2224 process_header( request, L"Sec-WebSocket-Key", key, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE, TRUE );
2225 free( key );
2226 return ERROR_SUCCESS;
2229 static DWORD send_request( struct request *request, const WCHAR *headers, DWORD headers_len, void *optional,
2230 DWORD optional_len, DWORD total_len, DWORD_PTR context, BOOL async )
2232 struct connect *connect = request->connect;
2233 struct session *session = connect->session;
2234 DWORD ret, len, buflen, content_length;
2235 WCHAR encoding[20];
2236 char *wire_req;
2237 int bytes_sent;
2238 BOOL chunked;
2240 TRACE( "request state %d.\n", request->state );
2242 request->read_reply_status = ERROR_WINHTTP_INCORRECT_HANDLE_STATE;
2243 request->read_reply_len = 0;
2244 request->state = REQUEST_RESPONSE_STATE_NONE;
2246 if (request->flags & REQUEST_FLAG_WEBSOCKET_UPGRADE
2247 && request->websocket_set_send_buffer_size < MIN_WEBSOCKET_SEND_BUFFER_SIZE)
2249 WARN( "Invalid send buffer size %u.\n", request->websocket_set_send_buffer_size );
2250 ret = ERROR_NOT_ENOUGH_MEMORY;
2251 goto end;
2254 drain_content( request );
2255 clear_response_headers( request );
2257 if (session->agent)
2258 process_header( request, L"User-Agent", session->agent, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2260 if (connect->hostname)
2261 add_host_header( request, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW );
2263 if (request->creds[TARGET_SERVER][SCHEME_BASIC].username)
2264 do_authorization( request, WINHTTP_AUTH_TARGET_SERVER, WINHTTP_AUTH_SCHEME_BASIC );
2266 buflen = sizeof(encoding);
2267 chunked = !query_headers( request, WINHTTP_QUERY_FLAG_REQUEST_HEADERS | WINHTTP_QUERY_TRANSFER_ENCODING,
2268 NULL, encoding, &buflen, NULL ) && !wcsicmp( encoding, L"chunked" );
2269 if (!chunked && (total_len || (request->verb && (!wcscmp( request->verb, L"POST" )
2270 || !wcscmp( request->verb, L"PUT" )))))
2272 WCHAR length[21]; /* decimal long int + null */
2273 swprintf( length, ARRAY_SIZE(length), L"%ld", total_len );
2274 process_header( request, L"Content-Length", length, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2276 if (request->flags & REQUEST_FLAG_WEBSOCKET_UPGRADE)
2278 request->websocket_send_buffer_size = request->websocket_set_send_buffer_size;
2279 process_header( request, L"Upgrade", L"websocket", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2280 process_header( request, L"Connection", L"Upgrade", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2281 process_header( request, L"Sec-WebSocket-Version", L"13", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2282 if ((ret = add_websocket_key_header( request ))) return ret;
2284 else if (!(request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE))
2286 process_header( request, L"Connection", L"Keep-Alive", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2288 if (request->hdr.flags & WINHTTP_FLAG_REFRESH)
2290 process_header( request, L"Pragma", L"no-cache", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2291 process_header( request, L"Cache-Control", L"no-cache", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
2293 if (headers && (ret = add_request_headers( request, headers, headers_len,
2294 WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE )))
2296 TRACE( "failed to add request headers: %lu\n", ret );
2297 return ret;
2299 if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES) && (ret = add_cookie_headers( request )))
2301 WARN( "failed to add cookie headers: %lu\n", ret );
2302 return ret;
2305 if (context) request->hdr.context = context;
2307 if ((ret = open_connection( request ))) goto end;
2308 if (!(wire_req = build_wire_request( request, &len )))
2310 ret = ERROR_OUTOFMEMORY;
2311 goto end;
2313 TRACE("full request: %s\n", debugstr_a(wire_req));
2315 request->state = REQUEST_RESPONSE_STATE_SENDING_REQUEST;
2316 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, NULL, 0 );
2318 ret = netconn_send( request->netconn, wire_req, len, &bytes_sent, NULL );
2319 free( wire_req );
2320 if (ret) goto end;
2322 if (optional_len)
2324 if ((ret = netconn_send( request->netconn, optional, optional_len, &bytes_sent, NULL ))) goto end;
2325 request->optional = optional;
2326 request->optional_len = optional_len;
2327 len += optional_len;
2329 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, &len, sizeof(len) );
2331 buflen = sizeof(content_length);
2332 if (query_headers( request, WINHTTP_QUERY_FLAG_REQUEST_HEADERS | WINHTTP_QUERY_CONTENT_LENGTH
2333 | WINHTTP_QUERY_FLAG_NUMBER, NULL, &content_length, &buflen, NULL ))
2334 content_length = total_len;
2336 if (!chunked && content_length <= optional_len)
2338 netconn_set_timeout( request->netconn, FALSE, request_receive_response_timeout( request ));
2339 request->read_reply_status = read_reply( request );
2340 if (request->state == REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED)
2341 request->state = REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED_REPLY_RECEIVED;
2342 else
2343 request->state = REQUEST_RESPONSE_STATE_REPLY_RECEIVED;
2345 else
2347 if (request->state == REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED)
2348 request->state = REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED_REQUEST_SENT;
2349 else
2350 request->state = REQUEST_RESPONSE_STATE_REQUEST_SENT;
2353 end:
2354 if (async)
2356 if (!ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, NULL, 0 );
2357 else
2359 WINHTTP_ASYNC_RESULT result;
2360 result.dwResult = API_SEND_REQUEST;
2361 result.dwError = ret;
2362 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
2365 return ret;
2368 static void task_send_request( void *ctx, BOOL abort )
2370 struct send_request *s = ctx;
2371 struct request *request = (struct request *)s->task_hdr.obj;
2373 if (abort) return;
2375 TRACE( "running %p\n", ctx );
2376 send_request( request, s->headers, s->headers_len, s->optional, s->optional_len, s->total_len, s->context, TRUE );
2378 free( s->headers );
2381 /***********************************************************************
2382 * WinHttpSendRequest (winhttp.@)
2384 BOOL WINAPI WinHttpSendRequest( HINTERNET hrequest, const WCHAR *headers, DWORD headers_len,
2385 void *optional, DWORD optional_len, DWORD total_len, DWORD_PTR context )
2387 DWORD ret;
2388 struct request *request;
2390 TRACE( "%p, %s, %lu, %p, %lu, %lu, %Ix\n", hrequest, debugstr_wn(headers, headers_len), headers_len, optional,
2391 optional_len, total_len, context );
2393 if (!(request = (struct request *)grab_object( hrequest )))
2395 SetLastError( ERROR_INVALID_HANDLE );
2396 return FALSE;
2398 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2400 release_object( &request->hdr );
2401 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2402 return FALSE;
2405 if (headers && !headers_len) headers_len = lstrlenW( headers );
2407 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
2409 struct send_request *s;
2411 if (!(s = malloc( sizeof(*s) )))
2413 release_object( &request->hdr );
2414 SetLastError( ERROR_OUTOFMEMORY );
2415 return FALSE;
2417 s->headers = wcsdup( headers );
2418 s->headers_len = headers_len;
2419 s->optional = optional;
2420 s->optional_len = optional_len;
2421 s->total_len = total_len;
2422 s->context = context;
2424 if ((ret = queue_task( &request->queue, task_send_request, &s->task_hdr, &request->hdr )))
2426 free( s->headers );
2427 free( s );
2430 else ret = send_request( request, headers, headers_len, optional, optional_len, total_len, context, FALSE );
2432 release_object( &request->hdr );
2433 SetLastError( ret );
2434 return !ret;
2437 static DWORD set_credentials( struct request *request, DWORD target, DWORD scheme_flag, const WCHAR *username,
2438 const WCHAR *password )
2440 enum auth_scheme scheme = scheme_from_flag( scheme_flag );
2442 if (scheme == SCHEME_INVALID || ((scheme == SCHEME_BASIC || scheme == SCHEME_DIGEST) && (!username || !password)))
2444 return ERROR_INVALID_PARAMETER;
2446 switch (target)
2448 case WINHTTP_AUTH_TARGET_SERVER:
2450 free( request->creds[TARGET_SERVER][scheme].username );
2451 if (!username) request->creds[TARGET_SERVER][scheme].username = NULL;
2452 else if (!(request->creds[TARGET_SERVER][scheme].username = wcsdup( username ))) return ERROR_OUTOFMEMORY;
2454 free( request->creds[TARGET_SERVER][scheme].password );
2455 if (!password) request->creds[TARGET_SERVER][scheme].password = NULL;
2456 else if (!(request->creds[TARGET_SERVER][scheme].password = wcsdup( password ))) return ERROR_OUTOFMEMORY;
2457 break;
2459 case WINHTTP_AUTH_TARGET_PROXY:
2461 free( request->creds[TARGET_PROXY][scheme].username );
2462 if (!username) request->creds[TARGET_PROXY][scheme].username = NULL;
2463 else if (!(request->creds[TARGET_PROXY][scheme].username = wcsdup( username ))) return ERROR_OUTOFMEMORY;
2465 free( request->creds[TARGET_PROXY][scheme].password );
2466 if (!password) request->creds[TARGET_PROXY][scheme].password = NULL;
2467 else if (!(request->creds[TARGET_PROXY][scheme].password = wcsdup( password ))) return ERROR_OUTOFMEMORY;
2468 break;
2470 default:
2471 WARN( "unknown target %lu\n", target );
2472 return ERROR_INVALID_PARAMETER;
2474 return ERROR_SUCCESS;
2477 /***********************************************************************
2478 * WinHttpSetCredentials (winhttp.@)
2480 BOOL WINAPI WinHttpSetCredentials( HINTERNET hrequest, DWORD target, DWORD scheme, const WCHAR *username,
2481 const WCHAR *password, void *params )
2483 DWORD ret;
2484 struct request *request;
2486 TRACE( "%p, %lu, %#lx, %s, %p, %p\n", hrequest, target, scheme, debugstr_w(username), password, params );
2488 if (!(request = (struct request *)grab_object( hrequest )))
2490 SetLastError( ERROR_INVALID_HANDLE );
2491 return FALSE;
2493 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
2495 release_object( &request->hdr );
2496 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
2497 return FALSE;
2500 ret = set_credentials( request, target, scheme, username, password );
2502 release_object( &request->hdr );
2503 SetLastError( ret );
2504 return !ret;
2507 static DWORD handle_authorization( struct request *request, DWORD status )
2509 DWORD ret, i, schemes, first, level, target;
2511 switch (status)
2513 case HTTP_STATUS_DENIED:
2514 target = WINHTTP_AUTH_TARGET_SERVER;
2515 level = WINHTTP_QUERY_WWW_AUTHENTICATE;
2516 break;
2518 case HTTP_STATUS_PROXY_AUTH_REQ:
2519 target = WINHTTP_AUTH_TARGET_PROXY;
2520 level = WINHTTP_QUERY_PROXY_AUTHENTICATE;
2521 break;
2523 default:
2524 ERR( "unhandled status %lu\n", status );
2525 return ERROR_WINHTTP_INTERNAL_ERROR;
2528 if ((ret = query_auth_schemes( request, level, &schemes, &first ))) return ret;
2529 if (do_authorization( request, target, first )) return ERROR_SUCCESS;
2531 schemes &= ~first;
2532 for (i = 0; i < ARRAY_SIZE( auth_schemes ); i++)
2534 if (!(schemes & auth_schemes[i].scheme)) continue;
2535 if (do_authorization( request, target, auth_schemes[i].scheme )) return ERROR_SUCCESS;
2537 return ERROR_WINHTTP_LOGIN_FAILURE;
2540 /* set the request content length based on the headers */
2541 static void set_content_length( struct request *request, DWORD status )
2543 WCHAR encoding[20];
2544 DWORD buflen = sizeof(request->content_length);
2546 if (status == HTTP_STATUS_NO_CONTENT || status == HTTP_STATUS_NOT_MODIFIED ||
2547 status == HTTP_STATUS_SWITCH_PROTOCOLS || !wcscmp( request->verb, L"HEAD" ))
2549 request->content_length = 0;
2551 else
2553 if (query_headers( request, WINHTTP_QUERY_CONTENT_LENGTH|WINHTTP_QUERY_FLAG_NUMBER,
2554 NULL, &request->content_length, &buflen, NULL ))
2555 request->content_length = ~0u;
2557 buflen = sizeof(encoding);
2558 if (!query_headers( request, WINHTTP_QUERY_TRANSFER_ENCODING, NULL, encoding, &buflen, NULL ) &&
2559 !wcsicmp( encoding, L"chunked" ))
2561 request->content_length = ~0u;
2562 request->read_chunked = TRUE;
2563 request->read_chunked_size = ~0u;
2564 request->read_chunked_eof = FALSE;
2567 request->content_read = 0;
2570 static DWORD read_line( struct request *request, char *buffer, DWORD *len )
2572 int count, bytes_read, pos = 0;
2573 DWORD ret;
2575 for (;;)
2577 char *eol = memchr( request->read_buf + request->read_pos, '\n', request->read_size );
2578 if (eol)
2580 count = eol - (request->read_buf + request->read_pos);
2581 bytes_read = count + 1;
2583 else count = bytes_read = request->read_size;
2585 count = min( count, *len - pos );
2586 memcpy( buffer + pos, request->read_buf + request->read_pos, count );
2587 pos += count;
2588 remove_data( request, bytes_read );
2589 if (eol) break;
2591 if ((ret = read_more_data( request, -1, FALSE ))) return ret;
2592 if (!request->read_size)
2594 *len = 0;
2595 TRACE("returning empty string\n");
2596 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
2599 if (pos < *len)
2601 if (pos && buffer[pos - 1] == '\r') pos--;
2602 *len = pos + 1;
2604 buffer[*len - 1] = 0;
2605 TRACE("returning %s\n", debugstr_a(buffer));
2606 return ERROR_SUCCESS;
2609 #define MAX_REPLY_LEN 1460
2610 #define INITIAL_HEADER_BUFFER_LEN 512
2612 static DWORD read_reply( struct request *request )
2614 char buffer[MAX_REPLY_LEN];
2615 DWORD ret, buflen, len, offset, crlf_len = 2; /* lstrlenW(crlf) */
2616 char *status_code, *status_text;
2617 WCHAR *versionW, *status_textW, *raw_headers;
2618 WCHAR status_codeW[4]; /* sizeof("nnn") */
2620 if (!request->netconn) return ERROR_WINHTTP_INCORRECT_HANDLE_STATE;
2624 buflen = MAX_REPLY_LEN;
2625 if ((ret = read_line( request, buffer, &buflen ))) return ret;
2627 /* first line should look like 'HTTP/1.x nnn OK' where nnn is the status code */
2628 if (!(status_code = strchr( buffer, ' ' ))) return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
2629 status_code++;
2630 if (!(status_text = strchr( status_code, ' ' ))) return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
2631 if ((len = status_text - status_code) != sizeof("nnn") - 1) return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
2632 status_text++;
2634 TRACE("version [%s] status code [%s] status text [%s]\n",
2635 debugstr_an(buffer, status_code - buffer - 1),
2636 debugstr_an(status_code, len),
2637 debugstr_a(status_text));
2639 } while (!memcmp( status_code, "100", len )); /* ignore "100 Continue" responses */
2641 /* we rely on the fact that the protocol is ascii */
2642 MultiByteToWideChar( CP_ACP, 0, status_code, len, status_codeW, len );
2643 status_codeW[len] = 0;
2644 if ((ret = process_header( request, L"Status", status_codeW,
2645 WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE, FALSE ))) return ret;
2647 len = status_code - buffer;
2648 if (!(versionW = malloc( len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
2649 MultiByteToWideChar( CP_ACP, 0, buffer, len - 1, versionW, len -1 );
2650 versionW[len - 1] = 0;
2652 free( request->version );
2653 request->version = versionW;
2655 len = buflen - (status_text - buffer);
2656 if (!(status_textW = malloc( len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
2657 MultiByteToWideChar( CP_ACP, 0, status_text, len, status_textW, len );
2659 free( request->status_text );
2660 request->status_text = status_textW;
2662 len = max( buflen + crlf_len, INITIAL_HEADER_BUFFER_LEN );
2663 if (!(raw_headers = malloc( len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
2664 MultiByteToWideChar( CP_ACP, 0, buffer, buflen, raw_headers, buflen );
2665 memcpy( raw_headers + buflen - 1, L"\r\n", sizeof(L"\r\n") );
2667 free( request->raw_headers );
2668 request->raw_headers = raw_headers;
2670 offset = buflen + crlf_len - 1;
2671 for (;;)
2673 struct header *header;
2674 int lenW;
2676 buflen = MAX_REPLY_LEN;
2677 if (read_line( request, buffer, &buflen )) return ERROR_SUCCESS;
2678 if (!*buffer) buflen = 1;
2680 while (len - offset < buflen + crlf_len)
2682 WCHAR *tmp;
2683 len *= 2;
2684 if (!(tmp = realloc( raw_headers, len * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
2685 request->raw_headers = raw_headers = tmp;
2687 if (!*buffer)
2689 memcpy( raw_headers + offset, L"\r\n", sizeof(L"\r\n") );
2690 break;
2692 lenW = MultiByteToWideChar( CP_ACP, 0, buffer, buflen, raw_headers + offset, buflen );
2694 if (!(header = parse_header( raw_headers + offset, lenW - 1 ))) break;
2695 if ((ret = process_header( request, header->field, header->value, WINHTTP_ADDREQ_FLAG_ADD, FALSE )))
2697 free_header( header );
2698 break;
2700 free_header( header );
2701 memcpy( raw_headers + offset + buflen - 1, L"\r\n", sizeof(L"\r\n") );
2702 offset += buflen + crlf_len - 1;
2705 TRACE("raw headers: %s\n", debugstr_w(raw_headers));
2706 return ret;
2709 static void record_cookies( struct request *request )
2711 unsigned int i;
2713 for (i = 0; i < request->num_headers; i++)
2715 struct header *set_cookie = &request->headers[i];
2716 if (!wcsicmp( set_cookie->field, L"Set-Cookie" ) && !set_cookie->is_request)
2718 set_cookies( request, set_cookie->value );
2723 static DWORD get_redirect_url( struct request *request, WCHAR **ret_url, DWORD *ret_len )
2725 DWORD size, ret;
2726 WCHAR *url;
2728 ret = query_headers( request, WINHTTP_QUERY_LOCATION, NULL, NULL, &size, NULL );
2729 if (ret != ERROR_INSUFFICIENT_BUFFER) return ret;
2730 if (!(url = malloc( size ))) return ERROR_OUTOFMEMORY;
2731 if ((ret = query_headers( request, WINHTTP_QUERY_LOCATION, NULL, url, &size, NULL )))
2733 free( url );
2734 return ret;
2736 *ret_url = url;
2737 *ret_len = size / sizeof(WCHAR);
2738 return ERROR_SUCCESS;
2741 static DWORD handle_redirect( struct request *request, DWORD status )
2743 DWORD ret, len, len_loc = 0;
2744 URL_COMPONENTS uc;
2745 struct connect *connect = request->connect;
2746 INTERNET_PORT port;
2747 WCHAR *hostname = NULL, *location = NULL;
2749 if ((ret = get_redirect_url( request, &location, &len_loc ))) return ret;
2751 memset( &uc, 0, sizeof(uc) );
2752 uc.dwStructSize = sizeof(uc);
2753 uc.dwSchemeLength = uc.dwHostNameLength = uc.dwUrlPathLength = uc.dwExtraInfoLength = ~0u;
2755 if (!WinHttpCrackUrl( location, len_loc, 0, &uc )) /* assume relative redirect */
2757 WCHAR *path, *p;
2759 ret = ERROR_OUTOFMEMORY;
2760 if (location[0] == '/')
2762 if (!(path = malloc( (len_loc + 1) * sizeof(WCHAR) ))) goto end;
2763 memcpy( path, location, len_loc * sizeof(WCHAR) );
2764 path[len_loc] = 0;
2766 else
2768 if ((p = wcsrchr( request->path, '/' ))) *p = 0;
2769 len = lstrlenW( request->path ) + 1 + len_loc;
2770 if (!(path = malloc( (len + 1) * sizeof(WCHAR) ))) goto end;
2771 lstrcpyW( path, request->path );
2772 lstrcatW( path, L"/" );
2773 memcpy( path + lstrlenW(path), location, len_loc * sizeof(WCHAR) );
2774 path[len_loc] = 0;
2776 free( request->path );
2777 request->path = path;
2778 ret = ERROR_SUCCESS;
2780 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, len_loc + 1 );
2782 else
2784 if (uc.nScheme == INTERNET_SCHEME_HTTP && request->hdr.flags & WINHTTP_FLAG_SECURE)
2786 if (request->hdr.redirect_policy == WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP)
2788 ret = ERROR_WINHTTP_REDIRECT_FAILED;
2789 goto end;
2791 TRACE("redirect from secure page to non-secure page\n");
2792 request->hdr.flags &= ~WINHTTP_FLAG_SECURE;
2794 else if (uc.nScheme == INTERNET_SCHEME_HTTPS && !(request->hdr.flags & WINHTTP_FLAG_SECURE))
2796 TRACE("redirect from non-secure page to secure page\n");
2797 request->hdr.flags |= WINHTTP_FLAG_SECURE;
2800 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, len_loc + 1 );
2802 len = uc.dwHostNameLength;
2803 if (!(hostname = malloc( (len + 1) * sizeof(WCHAR) )))
2805 ret = ERROR_OUTOFMEMORY;
2806 goto end;
2808 memcpy( hostname, uc.lpszHostName, len * sizeof(WCHAR) );
2809 hostname[len] = 0;
2811 port = uc.nPort ? uc.nPort : (uc.nScheme == INTERNET_SCHEME_HTTPS ? 443 : 80);
2812 if (wcsicmp( connect->hostname, hostname ) || connect->serverport != port)
2814 free( connect->hostname );
2815 connect->hostname = hostname;
2816 connect->hostport = port;
2817 if (!set_server_for_hostname( connect, hostname, port ))
2819 ret = ERROR_OUTOFMEMORY;
2820 goto end;
2823 netconn_release( request->netconn );
2824 request->netconn = NULL;
2825 request->content_length = request->content_read = 0;
2826 request->read_pos = request->read_size = 0;
2827 request->read_chunked = request->read_chunked_eof = FALSE;
2829 else free( hostname );
2831 if ((ret = add_host_header( request, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE ))) goto end;
2833 free( request->path );
2834 request->path = NULL;
2835 if (uc.dwUrlPathLength)
2837 len = uc.dwUrlPathLength + uc.dwExtraInfoLength;
2838 if (!(request->path = malloc( (len + 1) * sizeof(WCHAR) ))) goto end;
2839 memcpy( request->path, uc.lpszUrlPath, (len + 1) * sizeof(WCHAR) );
2840 request->path[len] = 0;
2842 else request->path = wcsdup( L"/" );
2845 if (status != HTTP_STATUS_REDIRECT_KEEP_VERB && !wcscmp( request->verb, L"POST" ))
2847 free( request->verb );
2848 request->verb = wcsdup( L"GET" );
2849 request->optional = NULL;
2850 request->optional_len = 0;
2853 end:
2854 free( location );
2855 return ret;
2858 static BOOL is_passport_request( struct request *request )
2860 static const WCHAR passportW[] = {'P','a','s','s','p','o','r','t','1','.','4'};
2861 WCHAR buf[1024];
2862 DWORD len = ARRAY_SIZE(buf);
2864 if (!(request->connect->session->passport_flags & WINHTTP_ENABLE_PASSPORT_AUTH) ||
2865 query_headers( request, WINHTTP_QUERY_WWW_AUTHENTICATE, NULL, buf, &len, NULL )) return FALSE;
2867 if (!wcsnicmp( buf, passportW, ARRAY_SIZE(passportW) ) &&
2868 (buf[ARRAY_SIZE(passportW)] == ' ' || !buf[ARRAY_SIZE(passportW)])) return TRUE;
2870 return FALSE;
2873 static DWORD handle_passport_redirect( struct request *request )
2875 DWORD ret, flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE;
2876 int i, len = lstrlenW( request->raw_headers );
2877 WCHAR *p = request->raw_headers;
2879 if ((ret = process_header( request, L"Status", L"401", flags, FALSE ))) return ret;
2881 for (i = 0; i < len; i++)
2883 if (i <= len - 3 && p[i] == '3' && p[i + 1] == '0' && p[i + 2] == '2')
2885 p[i] = '4';
2886 p[i + 2] = '1';
2887 break;
2890 return ERROR_SUCCESS;
2893 static void task_receive_response( void *ctx, BOOL abort );
2895 static DWORD queue_receive_response( struct request *request )
2897 struct receive_response *r;
2898 DWORD ret;
2900 if (!(r = malloc( sizeof(*r) ))) return ERROR_OUTOFMEMORY;
2901 if ((ret = queue_task( &request->queue, task_receive_response, &r->task_hdr, &request->hdr )))
2902 free( r );
2903 return ret;
2906 static DWORD receive_response( struct request *request )
2908 BOOL async_mode = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC;
2909 DWORD ret, size, query, status;
2911 TRACE( "request state %d.\n", request->state );
2913 switch (request->state)
2915 case REQUEST_RESPONSE_RECURSIVE_REQUEST:
2916 TRACE( "Sending request.\n" );
2917 if ((ret = send_request( request, NULL, 0, request->optional, request->optional_len, 0, 0, FALSE ))) goto done;
2918 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 );
2919 break;
2921 case REQUEST_RESPONSE_STATE_SENDING_REQUEST:
2922 if (!async_mode)
2924 ret = ERROR_WINHTTP_INCORRECT_HANDLE_STATE;
2925 goto done;
2927 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 );
2928 request->state = REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED;
2929 return queue_receive_response( request );
2932 case REQUEST_RESPONSE_STATE_REQUEST_SENT:
2933 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 );
2934 if (async_mode)
2936 request->state = REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED_REQUEST_SENT;
2937 return queue_receive_response( request );
2939 /* fallthrough */
2940 case REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED_REQUEST_SENT:
2941 netconn_set_timeout( request->netconn, FALSE, request_receive_response_timeout( request ));
2942 request->read_reply_status = read_reply( request );
2943 request->state = REQUEST_RESPONSE_STATE_REPLY_RECEIVED;
2944 break;
2946 case REQUEST_RESPONSE_STATE_REPLY_RECEIVED:
2947 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 );
2948 break;
2950 case REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED_REPLY_RECEIVED:
2951 request->state = REQUEST_RESPONSE_STATE_REPLY_RECEIVED;
2952 break;
2954 default:
2955 ret = ERROR_WINHTTP_INCORRECT_HANDLE_STATE;
2956 goto done;
2959 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED,
2960 &request->read_reply_len, sizeof(request->read_reply_len) );
2961 if ((ret = request->read_reply_status)) goto done;
2963 size = sizeof(DWORD);
2964 query = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER;
2965 if ((ret = query_headers( request, query, NULL, &status, &size, NULL ))) goto done;
2967 set_content_length( request, status );
2969 if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES)) record_cookies( request );
2971 if (status == HTTP_STATUS_REDIRECT && is_passport_request( request ))
2973 ret = handle_passport_redirect( request );
2974 goto done;
2976 if (status == HTTP_STATUS_MOVED || status == HTTP_STATUS_REDIRECT || status == HTTP_STATUS_REDIRECT_KEEP_VERB)
2978 if (request->hdr.disable_flags & WINHTTP_DISABLE_REDIRECTS ||
2979 request->hdr.redirect_policy == WINHTTP_OPTION_REDIRECT_POLICY_NEVER) goto done;
2981 if (++request->redirect_count > request->max_redirects)
2983 ret = ERROR_WINHTTP_REDIRECT_FAILED;
2984 goto done;
2987 if ((ret = handle_redirect( request, status ))) goto done;
2989 else if (status == HTTP_STATUS_DENIED || status == HTTP_STATUS_PROXY_AUTH_REQ)
2991 if (request->hdr.disable_flags & WINHTTP_DISABLE_AUTHENTICATION) goto done;
2993 if (handle_authorization( request, status )) goto done;
2995 else goto done;
2997 request->state = REQUEST_RESPONSE_RECURSIVE_REQUEST;
2998 return async_mode ? queue_receive_response( request ) : receive_response( request );
3000 done:
3001 if (!ret)
3003 request->state = REQUEST_RESPONSE_STATE_RESPONSE_RECEIVED;
3004 if (request->netconn) netconn_set_timeout( request->netconn, FALSE, request->receive_timeout );
3006 if (async_mode)
3008 if (!ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, NULL, 0 );
3009 else
3011 WINHTTP_ASYNC_RESULT result;
3012 result.dwResult = API_RECEIVE_RESPONSE;
3013 result.dwError = ret;
3014 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
3016 return ERROR_SUCCESS;
3018 return ret;
3021 static void task_receive_response( void *ctx, BOOL abort )
3023 struct receive_response *r = ctx;
3024 struct request *request = (struct request *)r->task_hdr.obj;
3026 if (abort) return;
3028 TRACE("running %p\n", ctx);
3029 receive_response( request );
3032 /***********************************************************************
3033 * WinHttpReceiveResponse (winhttp.@)
3035 BOOL WINAPI WinHttpReceiveResponse( HINTERNET hrequest, LPVOID reserved )
3037 DWORD ret;
3038 struct request *request;
3040 TRACE("%p, %p\n", hrequest, reserved);
3042 if (!(request = (struct request *)grab_object( hrequest )))
3044 SetLastError( ERROR_INVALID_HANDLE );
3045 return FALSE;
3047 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
3049 release_object( &request->hdr );
3050 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
3051 return FALSE;
3054 ret = receive_response( request );
3056 release_object( &request->hdr );
3057 SetLastError( ret );
3058 return !ret;
3061 static DWORD query_data_ready( struct request *request )
3063 DWORD count;
3065 count = get_available_data( request );
3066 if (!request->read_chunked && request->netconn) count += netconn_query_data_available( request->netconn );
3068 return count;
3071 static BOOL skip_async_queue( struct request *request, BOOL *wont_block, DWORD to_read )
3073 if (!request->read_chunked)
3074 to_read = min( to_read, request->content_length - request->content_read );
3075 *wont_block = end_of_read_data( request ) || query_data_ready( request ) >= to_read;
3076 return request->hdr.recursion_count < 3 && *wont_block;
3079 static DWORD query_data_available( struct request *request, DWORD *available, BOOL async )
3081 DWORD ret = ERROR_SUCCESS, count = 0;
3083 if (end_of_read_data( request )) goto done;
3085 if (!(count = query_data_ready( request )))
3087 if ((ret = refill_buffer( request, async ))) goto done;
3088 count = query_data_ready( request );
3091 done:
3092 TRACE( "%lu bytes available\n", count );
3093 if (async)
3095 if (!ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, &count, sizeof(count) );
3096 else
3098 WINHTTP_ASYNC_RESULT result;
3099 result.dwResult = API_QUERY_DATA_AVAILABLE;
3100 result.dwError = ret;
3101 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
3105 if (!ret && available) *available = count;
3106 return ret;
3109 static void task_query_data_available( void *ctx, BOOL abort )
3111 struct query_data *q = ctx;
3112 struct request *request = (struct request *)q->task_hdr.obj;
3114 if (abort) return;
3116 TRACE("running %p\n", ctx);
3117 query_data_available( request, q->available, TRUE );
3120 /***********************************************************************
3121 * WinHttpQueryDataAvailable (winhttp.@)
3123 BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available )
3125 DWORD ret;
3126 struct request *request;
3127 BOOL async;
3128 BOOL wont_block = FALSE;
3130 TRACE("%p, %p\n", hrequest, available);
3132 if (!(request = (struct request *)grab_object( hrequest )))
3134 SetLastError( ERROR_INVALID_HANDLE );
3135 return FALSE;
3137 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
3139 release_object( &request->hdr );
3140 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
3141 return FALSE;
3144 if (!(async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) || skip_async_queue( request, &wont_block, 1 ))
3146 ret = query_data_available( request, available, async );
3148 else if (wont_block)
3150 /* Data available but recursion limit reached, only queue callback. */
3151 struct send_callback *s;
3153 if (!(s = malloc( sizeof(*s) )))
3155 release_object( &request->hdr );
3156 SetLastError( ERROR_OUTOFMEMORY );
3157 return FALSE;
3160 if (!(ret = query_data_available( request, &s->count, FALSE )))
3162 if (available) *available = s->count;
3163 s->status = WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE;
3164 s->info = &s->count;
3165 s->buflen = sizeof(s->count);
3167 else
3169 s->result.dwResult = API_QUERY_DATA_AVAILABLE;
3170 s->result.dwError = ret;
3171 s->status = WINHTTP_CALLBACK_STATUS_REQUEST_ERROR;
3172 s->info = &s->result;
3173 s->buflen = sizeof(s->result);
3176 if ((ret = queue_task( &request->queue, task_send_callback, &s->task_hdr, &request->hdr )))
3177 free( s );
3178 else
3179 ret = ERROR_IO_PENDING;
3181 else
3183 struct query_data *q;
3185 if (!(q = malloc( sizeof(*q) )))
3187 release_object( &request->hdr );
3188 SetLastError( ERROR_OUTOFMEMORY );
3189 return FALSE;
3192 q->available = available;
3194 if ((ret = queue_task( &request->queue, task_query_data_available, &q->task_hdr, &request->hdr )))
3195 free( q );
3196 else
3197 ret = ERROR_IO_PENDING;
3200 release_object( &request->hdr );
3201 SetLastError( ret );
3202 return !ret || ret == ERROR_IO_PENDING;
3205 static void task_read_data( void *ctx, BOOL abort )
3207 struct read_data *r = ctx;
3208 struct request *request = (struct request *)r->task_hdr.obj;
3210 if (abort) return;
3212 TRACE("running %p\n", ctx);
3213 read_data( request, r->buffer, r->to_read, r->read, TRUE );
3216 /***********************************************************************
3217 * WinHttpReadData (winhttp.@)
3219 BOOL WINAPI WinHttpReadData( HINTERNET hrequest, void *buffer, DWORD to_read, DWORD *read )
3221 DWORD ret;
3222 struct request *request;
3223 BOOL async;
3224 BOOL wont_block = FALSE;
3226 TRACE( "%p, %p, %lu, %p\n", hrequest, buffer, to_read, read );
3228 if (!(request = (struct request *)grab_object( hrequest )))
3230 SetLastError( ERROR_INVALID_HANDLE );
3231 return FALSE;
3233 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
3235 release_object( &request->hdr );
3236 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
3237 return FALSE;
3240 if (!(async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) || skip_async_queue( request, &wont_block, to_read ))
3242 ret = read_data( request, buffer, to_read, read, async );
3244 else if (wont_block)
3246 /* Data available but recursion limit reached, only queue callback. */
3247 struct send_callback *s;
3249 if (!(s = malloc( sizeof(*s) )))
3251 release_object( &request->hdr );
3252 SetLastError( ERROR_OUTOFMEMORY );
3253 return FALSE;
3256 if (!(ret = read_data( request, buffer, to_read, &s->count, FALSE )))
3258 if (read) *read = s->count;
3259 s->status = WINHTTP_CALLBACK_STATUS_READ_COMPLETE;
3260 s->info = buffer;
3261 s->buflen = s->count;
3263 else
3265 s->result.dwResult = API_READ_DATA;
3266 s->result.dwError = ret;
3267 s->status = WINHTTP_CALLBACK_STATUS_REQUEST_ERROR;
3268 s->info = &s->result;
3269 s->buflen = sizeof(s->result);
3272 if ((ret = queue_task( &request->queue, task_send_callback, &s->task_hdr, &request->hdr )))
3273 free( s );
3274 else
3275 ret = ERROR_IO_PENDING;
3277 else
3279 struct read_data *r;
3281 if (!(r = malloc( sizeof(*r) )))
3283 release_object( &request->hdr );
3284 SetLastError( ERROR_OUTOFMEMORY );
3285 return FALSE;
3287 r->buffer = buffer;
3288 r->to_read = to_read;
3289 r->read = read;
3291 if ((ret = queue_task( &request->queue, task_read_data, &r->task_hdr, &request->hdr )))
3292 free( r );
3293 else
3294 ret = ERROR_IO_PENDING;
3297 release_object( &request->hdr );
3298 SetLastError( ret );
3299 return !ret || ret == ERROR_IO_PENDING;
3302 static DWORD write_data( struct request *request, const void *buffer, DWORD to_write, DWORD *written, BOOL async )
3304 DWORD ret;
3305 int num_bytes;
3307 ret = netconn_send( request->netconn, buffer, to_write, &num_bytes, NULL );
3309 if (async)
3311 if (!ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, &num_bytes, sizeof(num_bytes) );
3312 else
3314 WINHTTP_ASYNC_RESULT result;
3315 result.dwResult = API_WRITE_DATA;
3316 result.dwError = ret;
3317 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
3320 if (!ret && written) *written = num_bytes;
3321 return ret;
3324 static void task_write_data( void *ctx, BOOL abort )
3326 struct write_data *w = ctx;
3327 struct request *request = (struct request *)w->task_hdr.obj;
3329 if (abort) return;
3331 TRACE("running %p\n", ctx);
3332 write_data( request, w->buffer, w->to_write, w->written, TRUE );
3335 /***********************************************************************
3336 * WinHttpWriteData (winhttp.@)
3338 BOOL WINAPI WinHttpWriteData( HINTERNET hrequest, const void *buffer, DWORD to_write, DWORD *written )
3340 DWORD ret;
3341 struct request *request;
3343 TRACE( "%p, %p, %lu, %p\n", hrequest, buffer, to_write, written );
3345 if (!(request = (struct request *)grab_object( hrequest )))
3347 SetLastError( ERROR_INVALID_HANDLE );
3348 return FALSE;
3350 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
3352 release_object( &request->hdr );
3353 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
3354 return FALSE;
3357 if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
3359 struct write_data *w;
3361 if (!(w = malloc( sizeof(*w) )))
3363 release_object( &request->hdr );
3364 SetLastError( ERROR_OUTOFMEMORY );
3365 return FALSE;
3367 w->buffer = buffer;
3368 w->to_write = to_write;
3369 w->written = written;
3371 if ((ret = queue_task( &request->queue, task_write_data, &w->task_hdr, &request->hdr )))
3372 free( w );
3374 else ret = write_data( request, buffer, to_write, written, FALSE );
3376 release_object( &request->hdr );
3377 SetLastError( ret );
3378 return !ret;
3381 static void socket_handle_closing( struct object_header *hdr )
3383 struct socket *socket = (struct socket *)hdr;
3384 BOOL pending_tasks;
3386 pending_tasks = cancel_queue( &socket->send_q );
3387 pending_tasks = cancel_queue( &socket->recv_q ) || pending_tasks;
3389 if (pending_tasks)
3390 netconn_cancel_io( socket->netconn );
3393 static BOOL socket_query_option( struct object_header *hdr, DWORD option, void *buffer, DWORD *buflen )
3395 switch (option)
3397 case WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL:
3398 SetLastError( ERROR_INVALID_PARAMETER );
3399 return FALSE;
3402 FIXME( "unimplemented option %lu\n", option );
3403 SetLastError( ERROR_WINHTTP_INVALID_OPTION );
3404 return FALSE;
3407 static void socket_destroy( struct object_header *hdr )
3409 struct socket *socket = (struct socket *)hdr;
3411 TRACE("%p\n", socket);
3413 stop_queue( &socket->send_q );
3414 stop_queue( &socket->recv_q );
3416 netconn_release( socket->netconn );
3417 free( socket->read_buffer );
3418 free( socket->send_frame_buffer );
3419 free( socket );
3422 static BOOL socket_set_option( struct object_header *hdr, DWORD option, void *buffer, DWORD buflen )
3424 struct socket *socket = (struct socket *)hdr;
3426 switch (option)
3428 case WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL:
3430 DWORD interval;
3432 if (buflen != sizeof(DWORD) || (interval = *(DWORD *)buffer) < 15000)
3434 WARN( "Invalid parameters for WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL.\n" );
3435 SetLastError( ERROR_INVALID_PARAMETER );
3436 return FALSE;
3438 socket->keepalive_interval = interval;
3439 netconn_set_timeout( socket->netconn, FALSE, socket->keepalive_interval );
3440 SetLastError( ERROR_SUCCESS );
3441 TRACE( "WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL %lu.\n", interval);
3442 return TRUE;
3446 FIXME( "unimplemented option %lu\n", option );
3447 SetLastError( ERROR_WINHTTP_INVALID_OPTION );
3448 return FALSE;
3451 static const struct object_vtbl socket_vtbl =
3453 socket_handle_closing,
3454 socket_destroy,
3455 socket_query_option,
3456 socket_set_option,
3459 HINTERNET WINAPI WinHttpWebSocketCompleteUpgrade( HINTERNET hrequest, DWORD_PTR context )
3461 struct socket *socket;
3462 struct request *request;
3463 HINTERNET hsocket = NULL;
3465 TRACE( "%p, %Ix\n", hrequest, context );
3467 if (!(request = (struct request *)grab_object( hrequest )))
3469 SetLastError( ERROR_INVALID_HANDLE );
3470 return NULL;
3472 if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
3474 release_object( &request->hdr );
3475 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
3476 return NULL;
3478 if (!(socket = calloc( 1, sizeof(*socket) )))
3480 release_object( &request->hdr );
3481 return NULL;
3483 socket->hdr.type = WINHTTP_HANDLE_TYPE_SOCKET;
3484 socket->hdr.vtbl = &socket_vtbl;
3485 socket->hdr.refs = 1;
3486 socket->hdr.callback = request->hdr.callback;
3487 socket->hdr.notify_mask = request->hdr.notify_mask;
3488 socket->hdr.context = context;
3489 socket->hdr.flags = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC;
3490 socket->keepalive_interval = 30000;
3491 socket->send_buffer_size = request->websocket_send_buffer_size;
3492 if (request->read_size)
3494 if (!(socket->read_buffer = malloc( request->read_size )))
3496 ERR( "No memory.\n" );
3497 free( socket );
3498 release_object( &request->hdr );
3499 return NULL;
3501 socket->bytes_in_read_buffer = request->read_size;
3502 memcpy( socket->read_buffer, request->read_buf + request->read_pos, request->read_size );
3503 request->read_pos = request->read_size = 0;
3505 InitializeSRWLock( &socket->send_lock );
3506 init_queue( &socket->send_q );
3507 init_queue( &socket->recv_q );
3508 netconn_addref( request->netconn );
3509 socket->netconn = request->netconn;
3511 netconn_set_timeout( socket->netconn, FALSE, socket->keepalive_interval );
3513 if ((hsocket = alloc_handle( &socket->hdr )))
3515 send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, &hsocket, sizeof(hsocket) );
3518 release_object( &socket->hdr );
3519 release_object( &request->hdr );
3520 TRACE("returning %p\n", hsocket);
3521 if (hsocket) SetLastError( ERROR_SUCCESS );
3522 return hsocket;
3525 static DWORD send_bytes( struct socket *socket, char *bytes, int len, int *sent, WSAOVERLAPPED *ovr )
3527 int count;
3528 DWORD err;
3529 err = netconn_send( socket->netconn, bytes, len, &count, ovr );
3530 if (sent) *sent = count;
3531 if (err) return err;
3532 return (count == len || (ovr && count)) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR;
3535 #define FIN_BIT (1 << 7)
3536 #define MASK_BIT (1 << 7)
3537 #define RESERVED_BIT (7 << 4)
3538 #define CONTROL_BIT (1 << 3)
3540 static DWORD send_frame( struct socket *socket, enum socket_opcode opcode, USHORT status, const char *buf,
3541 DWORD buflen, BOOL final, WSAOVERLAPPED *ovr )
3543 DWORD i, offset = 2, len = buflen, buffer_size, ret = 0;
3544 int sent_size;
3545 char hdr[14];
3546 char *ptr;
3548 TRACE( "sending %02x frame, len %lu\n", opcode, len );
3550 if (opcode == SOCKET_OPCODE_CLOSE) len += sizeof(status);
3552 hdr[0] = final ? (char)FIN_BIT : 0;
3553 hdr[0] |= opcode;
3554 hdr[1] = (char)MASK_BIT;
3555 if (len < 126) hdr[1] |= len;
3556 else if (len < 65536)
3558 hdr[1] |= 126;
3559 hdr[2] = len >> 8;
3560 hdr[3] = len & 0xff;
3561 offset += 2;
3563 else
3565 hdr[1] |= 127;
3566 hdr[2] = hdr[3] = hdr[4] = hdr[5] = 0;
3567 hdr[6] = len >> 24;
3568 hdr[7] = (len >> 16) & 0xff;
3569 hdr[8] = (len >> 8) & 0xff;
3570 hdr[9] = len & 0xff;
3571 offset += 8;
3574 buffer_size = len + offset + 4;
3575 assert( buffer_size - len < socket->send_buffer_size );
3576 if (buffer_size > socket->send_frame_buffer_size && socket->send_frame_buffer_size < socket->send_buffer_size)
3578 DWORD new_size;
3579 void *new;
3581 new_size = min( buffer_size, socket->send_buffer_size );
3582 if (!(new = realloc( socket->send_frame_buffer, new_size )))
3584 ERR( "out of memory, buffer_size %lu\n", buffer_size);
3585 return ERROR_OUTOFMEMORY;
3587 socket->send_frame_buffer = new;
3588 socket->send_frame_buffer_size = new_size;
3590 ptr = socket->send_frame_buffer;
3592 memcpy(ptr, hdr, offset);
3593 ptr += offset;
3595 RtlGenRandom( socket->mask, 4 );
3596 memcpy( ptr, socket->mask, 4 );
3597 ptr += 4;
3598 socket->mask_index = 0;
3600 if (opcode == SOCKET_OPCODE_CLOSE) /* prepend status code */
3602 *ptr++ = (status >> 8) ^ socket->mask[socket->mask_index++ % 4];
3603 *ptr++ = (status & 0xff) ^ socket->mask[socket->mask_index++ % 4];
3606 offset = ptr - socket->send_frame_buffer;
3607 socket->send_remaining_size = offset + buflen;
3608 socket->client_buffer_offset = 0;
3609 while (socket->send_remaining_size)
3611 len = min( buflen, socket->send_buffer_size - offset );
3612 for (i = 0; i < len; ++i)
3614 socket->send_frame_buffer[offset++] = buf[socket->client_buffer_offset++]
3615 ^ socket->mask[socket->mask_index++ % 4];
3618 sent_size = 0;
3619 ret = send_bytes( socket, socket->send_frame_buffer, offset, &sent_size, ovr );
3620 socket->send_remaining_size -= sent_size;
3621 if (ret)
3623 if (ovr && ret == WSA_IO_PENDING)
3625 memmove( socket->send_frame_buffer, socket->send_frame_buffer + sent_size, offset - sent_size );
3626 socket->bytes_in_send_frame_buffer = offset - sent_size;
3628 return ret;
3630 assert( sent_size == offset );
3631 offset = 0;
3632 buflen -= len;
3634 return ERROR_SUCCESS;
3637 static DWORD complete_send_frame( struct socket *socket, WSAOVERLAPPED *ovr, const char *buf )
3639 DWORD ret, len, i;
3641 if (!netconn_wait_overlapped_result( socket->netconn, ovr, &len ))
3642 return WSAGetLastError();
3644 if (socket->bytes_in_send_frame_buffer)
3646 ret = send_bytes( socket, socket->send_frame_buffer, socket->bytes_in_send_frame_buffer, NULL, NULL );
3647 if (ret) return ret;
3650 assert( socket->bytes_in_send_frame_buffer <= socket->send_remaining_size );
3651 socket->send_remaining_size -= socket->bytes_in_send_frame_buffer;
3653 while (socket->send_remaining_size)
3655 len = min( socket->send_remaining_size, socket->send_buffer_size );
3656 for (i = 0; i < len; ++i)
3658 socket->send_frame_buffer[i] = buf[socket->client_buffer_offset++]
3659 ^ socket->mask[socket->mask_index++ % 4];
3661 ret = send_bytes( socket, socket->send_frame_buffer, len, NULL, NULL );
3662 if (ret) return ret;
3663 socket->send_remaining_size -= len;
3665 return ERROR_SUCCESS;
3668 static void send_io_complete( struct object_header *hdr )
3670 LONG count = InterlockedDecrement( &hdr->pending_sends );
3671 assert( count >= 0 );
3674 /* returns FALSE if sending callback should be omitted. */
3675 static void receive_io_complete( struct socket *socket )
3677 LONG count = InterlockedDecrement( &socket->hdr.pending_receives );
3678 assert( count >= 0 );
3681 static BOOL socket_can_send( struct socket *socket )
3683 return socket->state == SOCKET_STATE_OPEN && !socket->close_frame_received;
3686 static BOOL socket_can_receive( struct socket *socket )
3688 return socket->state <= SOCKET_STATE_SHUTDOWN && !socket->close_frame_received;
3691 static BOOL validate_buffer_type( WINHTTP_WEB_SOCKET_BUFFER_TYPE type, enum fragment_type current_fragment )
3693 switch (current_fragment)
3695 case SOCKET_FRAGMENT_NONE:
3696 return type == WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE
3697 || type == WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE
3698 || type == WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE
3699 || type == WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE;
3700 case SOCKET_FRAGMENT_BINARY:
3701 return type == WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE
3702 || type == WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE;
3703 case SOCKET_FRAGMENT_UTF8:
3704 return type == WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE
3705 || type == WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE;
3707 assert( 0 );
3708 return FALSE;
3711 static enum socket_opcode map_buffer_type( struct socket *socket, WINHTTP_WEB_SOCKET_BUFFER_TYPE type )
3713 switch (type)
3715 case WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE:
3716 if (socket->sending_fragment_type)
3718 socket->sending_fragment_type = SOCKET_FRAGMENT_NONE;
3719 return SOCKET_OPCODE_CONTINUE;
3721 return SOCKET_OPCODE_TEXT;
3723 case WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE:
3724 if (socket->sending_fragment_type)
3726 socket->sending_fragment_type = SOCKET_FRAGMENT_NONE;
3727 return SOCKET_OPCODE_CONTINUE;
3729 return SOCKET_OPCODE_BINARY;
3731 case WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE:
3732 if (!socket->sending_fragment_type)
3734 socket->sending_fragment_type = SOCKET_FRAGMENT_UTF8;
3735 return SOCKET_OPCODE_TEXT;
3737 return SOCKET_OPCODE_CONTINUE;
3739 case WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE:
3740 if (!socket->sending_fragment_type)
3742 socket->sending_fragment_type = SOCKET_FRAGMENT_BINARY;
3743 return SOCKET_OPCODE_BINARY;
3745 return SOCKET_OPCODE_CONTINUE;
3747 case WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE:
3748 return SOCKET_OPCODE_CLOSE;
3750 default:
3751 FIXME("buffer type %u not supported\n", type);
3752 return SOCKET_OPCODE_INVALID;
3756 static void socket_send_complete( struct socket *socket, DWORD ret, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, DWORD len )
3758 if (!ret)
3760 WINHTTP_WEB_SOCKET_STATUS status;
3761 status.dwBytesTransferred = len;
3762 status.eBufferType = type;
3763 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, &status, sizeof(status) );
3765 else
3767 WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
3768 result.AsyncResult.dwResult = API_WRITE_DATA;
3769 result.AsyncResult.dwError = ret;
3770 result.Operation = WINHTTP_WEB_SOCKET_SEND_OPERATION;
3771 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
3775 static DWORD socket_send( struct socket *socket, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, const void *buf, DWORD len,
3776 WSAOVERLAPPED *ovr )
3778 enum socket_opcode opcode = map_buffer_type( socket, type );
3779 BOOL final = (type != WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE &&
3780 type != WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE);
3782 return send_frame( socket, opcode, 0, buf, len, final, ovr );
3785 static void task_socket_send( void *ctx, BOOL abort )
3787 struct socket_send *s = ctx;
3788 struct socket *socket = (struct socket *)s->task_hdr.obj;
3789 DWORD ret;
3791 if (abort) return;
3793 TRACE("running %p\n", ctx);
3795 if (s->complete_async) ret = complete_send_frame( socket, &s->ovr, s->buf );
3796 else ret = socket_send( socket, s->type, s->buf, s->len, NULL );
3798 send_io_complete( &socket->hdr );
3799 InterlockedExchange( &socket->pending_noncontrol_send, 0 );
3800 socket_send_complete( socket, ret, s->type, s->len );
3803 DWORD WINAPI WinHttpWebSocketSend( HINTERNET hsocket, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, void *buf, DWORD len )
3805 struct socket *socket;
3806 DWORD ret = 0;
3808 TRACE( "%p, %u, %p, %lu\n", hsocket, type, buf, len );
3810 if (len && !buf) return ERROR_INVALID_PARAMETER;
3812 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
3813 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
3815 release_object( &socket->hdr );
3816 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
3818 if (!socket_can_send( socket ))
3820 release_object( &socket->hdr );
3821 return ERROR_INVALID_OPERATION;
3824 if (socket->hdr.flags & WINHTTP_FLAG_ASYNC)
3826 BOOL async_send, complete_async = FALSE;
3827 struct socket_send *s;
3829 if (InterlockedCompareExchange( &socket->pending_noncontrol_send, 1, 0 ))
3831 WARN( "Previous send is still queued.\n" );
3832 release_object( &socket->hdr );
3833 return ERROR_INVALID_OPERATION;
3835 if (!validate_buffer_type( type, socket->sending_fragment_type ))
3837 WARN( "Invalid buffer type %u, sending_fragment_type %u.\n", type, socket->sending_fragment_type );
3838 InterlockedExchange( &socket->pending_noncontrol_send, 0 );
3839 release_object( &socket->hdr );
3840 return ERROR_INVALID_PARAMETER;
3843 if (!(s = malloc( sizeof(*s) )))
3845 InterlockedExchange( &socket->pending_noncontrol_send, 0 );
3846 release_object( &socket->hdr );
3847 return ERROR_OUTOFMEMORY;
3850 AcquireSRWLockExclusive( &socket->send_lock );
3851 async_send = InterlockedIncrement( &socket->hdr.pending_sends ) > 1 || socket->hdr.recursion_count >= 3;
3852 if (!async_send)
3854 memset( &s->ovr, 0, sizeof(s->ovr) );
3855 if ((ret = socket_send( socket, type, buf, len, &s->ovr )) == WSA_IO_PENDING)
3857 async_send = TRUE;
3858 complete_async = TRUE;
3862 if (async_send)
3864 s->complete_async = complete_async;
3865 TRACE("queueing, complete_async %#x.\n", complete_async);
3866 s->type = type;
3867 s->buf = buf;
3868 s->len = len;
3870 if ((ret = queue_task( &socket->send_q, task_socket_send, &s->task_hdr, &socket->hdr )))
3871 free( s );
3873 if (!async_send || ret)
3875 InterlockedDecrement( &socket->hdr.pending_sends );
3876 InterlockedExchange( &socket->pending_noncontrol_send, 0 );
3878 ReleaseSRWLockExclusive( &socket->send_lock );
3879 if (!async_send)
3881 TRACE("sent sync.\n");
3882 free( s );
3883 socket_send_complete( socket, ret, type, len );
3884 ret = ERROR_SUCCESS;
3887 else
3889 if (validate_buffer_type( type, socket->sending_fragment_type ))
3891 ret = socket_send( socket, type, buf, len, NULL );
3893 else
3895 WARN( "Invalid buffer type %u, sending_fragment_type %u.\n", type, socket->sending_fragment_type );
3896 ret = ERROR_INVALID_PARAMETER;
3900 release_object( &socket->hdr );
3901 return ret;
3904 static DWORD receive_bytes( struct socket *socket, char *buf, DWORD len, DWORD *ret_len, BOOL read_full_buffer )
3906 DWORD err, size = 0, needed = len;
3907 char *ptr = buf;
3908 int received;
3910 if (socket->bytes_in_read_buffer)
3912 size = min( needed, socket->bytes_in_read_buffer );
3913 memcpy( ptr, socket->read_buffer, size );
3914 memmove( socket->read_buffer, socket->read_buffer + size, socket->bytes_in_read_buffer - size );
3915 socket->bytes_in_read_buffer -= size;
3916 needed -= size;
3917 ptr += size;
3919 while (size != len)
3921 if ((err = netconn_recv( socket->netconn, ptr, needed, 0, &received ))) return err;
3922 if (!received) break;
3923 size += received;
3924 if (!read_full_buffer) break;
3925 needed -= received;
3926 ptr += received;
3928 *ret_len = size;
3929 if (size != len && (read_full_buffer || !size)) return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
3930 return ERROR_SUCCESS;
3933 static BOOL is_supported_opcode( enum socket_opcode opcode )
3935 switch (opcode)
3937 case SOCKET_OPCODE_CONTINUE:
3938 case SOCKET_OPCODE_TEXT:
3939 case SOCKET_OPCODE_BINARY:
3940 case SOCKET_OPCODE_CLOSE:
3941 case SOCKET_OPCODE_PING:
3942 case SOCKET_OPCODE_PONG:
3943 return TRUE;
3944 default:
3945 FIXME( "opcode %02x not handled\n", opcode );
3946 return FALSE;
3950 static DWORD receive_frame( struct socket *socket, DWORD *ret_len, enum socket_opcode *opcode, BOOL *final )
3952 DWORD ret, len, count;
3953 char hdr[2];
3955 if ((ret = receive_bytes( socket, hdr, sizeof(hdr), &count, TRUE ))) return ret;
3956 if ((hdr[0] & RESERVED_BIT) || (hdr[1] & MASK_BIT) || !is_supported_opcode( hdr[0] & 0xf ))
3958 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
3960 *opcode = hdr[0] & 0xf;
3961 *final = hdr[0] & FIN_BIT;
3962 TRACE("received %02x frame, final %#x\n", *opcode, *final);
3964 len = hdr[1] & ~MASK_BIT;
3965 if (len == 126)
3967 USHORT len16;
3968 if ((ret = receive_bytes( socket, (char *)&len16, sizeof(len16), &count, TRUE ))) return ret;
3969 len = RtlUshortByteSwap( len16 );
3971 else if (len == 127)
3973 ULONGLONG len64;
3974 if ((ret = receive_bytes( socket, (char *)&len64, sizeof(len64), &count, TRUE ))) return ret;
3975 if ((len64 = RtlUlonglongByteSwap( len64 )) > ~0u) return ERROR_NOT_SUPPORTED;
3976 len = len64;
3979 *ret_len = len;
3980 return ERROR_SUCCESS;
3983 static void task_socket_send_pong( void *ctx, BOOL abort )
3985 struct socket_send *s = ctx;
3986 struct socket *socket = (struct socket *)s->task_hdr.obj;
3988 if (abort) return;
3990 TRACE("running %p\n", ctx);
3992 if (s->complete_async) complete_send_frame( socket, &s->ovr, NULL );
3993 else send_frame( socket, SOCKET_OPCODE_PONG, 0, NULL, 0, TRUE, NULL );
3995 send_io_complete( &socket->hdr );
3998 static DWORD socket_send_pong( struct socket *socket )
4000 if (socket->hdr.flags & WINHTTP_FLAG_ASYNC)
4002 BOOL async_send, complete_async = FALSE;
4003 struct socket_send *s;
4004 DWORD ret = 0;
4006 if (!(s = malloc( sizeof(*s) ))) return ERROR_OUTOFMEMORY;
4008 AcquireSRWLockExclusive( &socket->send_lock );
4009 async_send = InterlockedIncrement( &socket->hdr.pending_sends ) > 1;
4010 if (!async_send)
4012 memset( &s->ovr, 0, sizeof(s->ovr) );
4013 if ((ret = send_frame( socket, SOCKET_OPCODE_PONG, 0, NULL, 0, TRUE, &s->ovr )) == WSA_IO_PENDING)
4015 async_send = TRUE;
4016 complete_async = TRUE;
4020 if (async_send)
4022 s->complete_async = complete_async;
4023 if ((ret = queue_task( &socket->send_q, task_socket_send_pong, &s->task_hdr, &socket->hdr )))
4025 InterlockedDecrement( &socket->hdr.pending_sends );
4026 free( s );
4029 else
4031 InterlockedDecrement( &socket->hdr.pending_sends );
4032 free( s );
4034 ReleaseSRWLockExclusive( &socket->send_lock );
4035 return ret;
4037 return send_frame( socket, SOCKET_OPCODE_PONG, 0, NULL, 0, TRUE, NULL );
4040 static DWORD socket_drain( struct socket *socket )
4042 DWORD ret, count;
4044 while (socket->read_size)
4046 char buf[1024];
4047 if ((ret = receive_bytes( socket, buf, min(socket->read_size, sizeof(buf)), &count, TRUE ))) return ret;
4048 socket->read_size -= count;
4050 return ERROR_SUCCESS;
4053 static DWORD receive_close_status( struct socket *socket, DWORD len )
4055 DWORD reason_len, ret;
4057 socket->close_frame_received = TRUE;
4058 if ((len && (len < sizeof(socket->status) || len > sizeof(socket->status) + sizeof(socket->reason))))
4059 return (socket->close_frame_receive_err = ERROR_WINHTTP_INVALID_SERVER_RESPONSE);
4061 if (!len) return (socket->close_frame_receive_err = ERROR_SUCCESS);
4063 reason_len = len - sizeof(socket->status);
4064 if ((ret = receive_bytes( socket, (char *)&socket->status, sizeof(socket->status), &len, TRUE )))
4065 return (socket->close_frame_receive_err = ret);
4066 socket->status = RtlUshortByteSwap( socket->status );
4067 return (socket->close_frame_receive_err
4068 = receive_bytes( socket, socket->reason, reason_len, &socket->reason_len, TRUE ));
4071 static DWORD handle_control_frame( struct socket *socket )
4073 DWORD ret;
4075 TRACE( "opcode %u.\n", socket->opcode );
4077 switch (socket->opcode)
4079 case SOCKET_OPCODE_PING:
4080 return socket_send_pong( socket );
4082 case SOCKET_OPCODE_PONG:
4083 return socket_drain( socket );
4085 case SOCKET_OPCODE_CLOSE:
4086 if (socket->state < SOCKET_STATE_SHUTDOWN)
4087 WARN( "SOCKET_OPCODE_CLOSE received, socket->state %u.\n", socket->state );
4088 if (socket->close_frame_received)
4090 FIXME( "Close frame already received.\n" );
4091 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
4094 ret = receive_close_status( socket, socket->read_size );
4095 socket->read_size = 0;
4096 return ret;
4098 default:
4099 ERR("unhandled control opcode %02x\n", socket->opcode);
4100 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
4103 return ERROR_SUCCESS;
4106 static WINHTTP_WEB_SOCKET_BUFFER_TYPE map_opcode( struct socket *socket, enum socket_opcode opcode, BOOL fragment )
4108 enum fragment_type frag_type = socket->receiving_fragment_type;
4110 switch (opcode)
4112 case SOCKET_OPCODE_TEXT:
4113 if (frag_type && frag_type != SOCKET_FRAGMENT_UTF8)
4114 FIXME( "Received SOCKET_OPCODE_TEXT with prev fragment %u.\n", frag_type );
4115 if (fragment)
4117 socket->receiving_fragment_type = SOCKET_FRAGMENT_UTF8;
4118 return WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE;
4120 socket->receiving_fragment_type = SOCKET_FRAGMENT_NONE;
4121 return WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE;
4123 case SOCKET_OPCODE_BINARY:
4124 if (frag_type && frag_type != SOCKET_FRAGMENT_BINARY)
4125 FIXME( "Received SOCKET_OPCODE_BINARY with prev fragment %u.\n", frag_type );
4126 if (fragment)
4128 socket->receiving_fragment_type = SOCKET_FRAGMENT_BINARY;
4129 return WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE;
4131 socket->receiving_fragment_type = SOCKET_FRAGMENT_NONE;
4132 return WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE;
4134 case SOCKET_OPCODE_CONTINUE:
4135 if (!frag_type)
4137 FIXME( "Received SOCKET_OPCODE_CONTINUE without starting fragment.\n" );
4138 return ~0u;
4140 if (fragment)
4142 return frag_type == SOCKET_FRAGMENT_BINARY ? WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE
4143 : WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE;
4145 socket->receiving_fragment_type = SOCKET_FRAGMENT_NONE;
4146 return frag_type == SOCKET_FRAGMENT_BINARY ? WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE
4147 : WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE;
4149 case SOCKET_OPCODE_CLOSE:
4150 return WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE;
4152 default:
4153 FIXME("opcode %02x not handled\n", opcode);
4154 return ~0u;
4158 static DWORD socket_receive( struct socket *socket, void *buf, DWORD len, DWORD *ret_len,
4159 WINHTTP_WEB_SOCKET_BUFFER_TYPE *ret_type )
4161 BOOL final = socket->last_receive_final;
4162 DWORD count, ret = ERROR_SUCCESS;
4164 if (!socket->read_size)
4166 for (;;)
4168 if (!(ret = receive_frame( socket, &socket->read_size, &socket->opcode, &final )))
4170 if (!(socket->opcode & CONTROL_BIT) || (ret = handle_control_frame( socket ))
4171 || socket->opcode == SOCKET_OPCODE_CLOSE) break;
4173 else if (ret == WSAETIMEDOUT) ret = socket_send_pong( socket );
4174 if (ret) break;
4177 if (!ret)
4179 socket->last_receive_final = final;
4180 ret = receive_bytes( socket, buf, min(len, socket->read_size), &count, FALSE );
4182 if (!ret)
4184 if (count < socket->read_size)
4185 WARN("Short read.\n");
4187 socket->read_size -= count;
4188 *ret_len = count;
4189 *ret_type = map_opcode( socket, socket->opcode, !final || socket->read_size != 0 );
4190 TRACE( "len %lu, *ret_len %lu, *ret_type %u.\n", len, *ret_len, *ret_type );
4191 if (*ret_type == ~0u)
4193 FIXME( "Unexpected opcode %u.\n", socket->opcode );
4194 socket->read_size = 0;
4195 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
4198 return ret;
4201 static void socket_receive_complete( struct socket *socket, DWORD ret, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, DWORD len )
4203 if (!ret)
4205 WINHTTP_WEB_SOCKET_STATUS status;
4206 status.dwBytesTransferred = len;
4207 status.eBufferType = type;
4208 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, &status, sizeof(status) );
4210 else
4212 WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
4213 result.AsyncResult.dwResult = 0;
4214 result.AsyncResult.dwError = ret;
4215 result.Operation = WINHTTP_WEB_SOCKET_RECEIVE_OPERATION;
4216 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
4220 static void task_socket_receive( void *ctx, BOOL abort )
4222 struct socket_receive *r = ctx;
4223 struct socket *socket = (struct socket *)r->task_hdr.obj;
4224 DWORD ret, count;
4225 WINHTTP_WEB_SOCKET_BUFFER_TYPE type;
4227 if (abort)
4229 socket_receive_complete( socket, ERROR_WINHTTP_OPERATION_CANCELLED, 0, 0 );
4230 return;
4233 TRACE("running %p\n", ctx);
4234 ret = socket_receive( socket, r->buf, r->len, &count, &type );
4235 receive_io_complete( socket );
4236 if (task_needs_completion( &r->task_hdr ))
4237 socket_receive_complete( socket, ret, type, count );
4240 DWORD WINAPI WinHttpWebSocketReceive( HINTERNET hsocket, void *buf, DWORD len, DWORD *ret_len,
4241 WINHTTP_WEB_SOCKET_BUFFER_TYPE *ret_type )
4243 struct socket *socket;
4244 DWORD ret;
4246 TRACE( "%p, %p, %lu, %p, %p\n", hsocket, buf, len, ret_len, ret_type );
4248 if (!buf || !len) return ERROR_INVALID_PARAMETER;
4250 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
4251 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
4253 release_object( &socket->hdr );
4254 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
4256 if (!socket_can_receive( socket ))
4258 release_object( &socket->hdr );
4259 return ERROR_INVALID_OPERATION;
4262 if (socket->hdr.flags & WINHTTP_FLAG_ASYNC)
4264 struct socket_receive *r;
4266 if (InterlockedIncrement( &socket->hdr.pending_receives ) > 1)
4268 InterlockedDecrement( &socket->hdr.pending_receives );
4269 WARN( "Attempt to queue receive while another is pending.\n" );
4270 release_object( &socket->hdr );
4271 return ERROR_INVALID_OPERATION;
4274 if (!(r = malloc( sizeof(*r) )))
4276 InterlockedDecrement( &socket->hdr.pending_receives );
4277 release_object( &socket->hdr );
4278 return ERROR_OUTOFMEMORY;
4280 r->buf = buf;
4281 r->len = len;
4283 if ((ret = queue_task( &socket->recv_q, task_socket_receive, &r->task_hdr, &socket->hdr )))
4285 InterlockedDecrement( &socket->hdr.pending_receives );
4286 free( r );
4289 else ret = socket_receive( socket, buf, len, ret_len, ret_type );
4291 release_object( &socket->hdr );
4292 return ret;
4295 static void socket_shutdown_complete( struct socket *socket, DWORD ret )
4297 if (!ret) send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE, NULL, 0 );
4298 else
4300 WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
4301 result.AsyncResult.dwResult = API_WRITE_DATA;
4302 result.AsyncResult.dwError = ret;
4303 result.Operation = WINHTTP_WEB_SOCKET_SHUTDOWN_OPERATION;
4304 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
4308 static void task_socket_shutdown( void *ctx, BOOL abort )
4310 struct socket_shutdown *s = ctx;
4311 struct socket *socket = (struct socket *)s->task_hdr.obj;
4312 DWORD ret;
4314 if (abort) return;
4316 TRACE("running %p\n", ctx);
4318 if (s->complete_async) ret = complete_send_frame( socket, &s->ovr, s->reason );
4319 else ret = send_frame( socket, SOCKET_OPCODE_CLOSE, s->status, s->reason, s->len, TRUE, NULL );
4321 send_io_complete( &socket->hdr );
4322 if (s->send_callback) socket_shutdown_complete( socket, ret );
4325 static DWORD send_socket_shutdown( struct socket *socket, USHORT status, const void *reason, DWORD len,
4326 BOOL send_callback)
4328 DWORD ret;
4330 if (socket->state < SOCKET_STATE_SHUTDOWN) socket->state = SOCKET_STATE_SHUTDOWN;
4332 if (socket->hdr.flags & WINHTTP_FLAG_ASYNC)
4334 BOOL async_send, complete_async = FALSE;
4335 struct socket_shutdown *s;
4337 if (!(s = malloc( sizeof(*s) ))) return FALSE;
4339 AcquireSRWLockExclusive( &socket->send_lock );
4340 async_send = InterlockedIncrement( &socket->hdr.pending_sends ) > 1 || socket->hdr.recursion_count >= 3;
4341 if (!async_send)
4343 memset( &s->ovr, 0, sizeof(s->ovr) );
4344 if ((ret = send_frame( socket, SOCKET_OPCODE_CLOSE, status, reason, len, TRUE, &s->ovr )) == WSA_IO_PENDING)
4346 async_send = TRUE;
4347 complete_async = TRUE;
4351 if (async_send)
4353 s->complete_async = complete_async;
4354 s->status = status;
4355 memcpy( s->reason, reason, len );
4356 s->len = len;
4357 s->send_callback = send_callback;
4359 if ((ret = queue_task( &socket->send_q, task_socket_shutdown, &s->task_hdr, &socket->hdr )))
4361 InterlockedDecrement( &socket->hdr.pending_sends );
4362 free( s );
4365 else InterlockedDecrement( &socket->hdr.pending_sends );
4366 ReleaseSRWLockExclusive( &socket->send_lock );
4367 if (!async_send)
4369 free( s );
4370 if (send_callback)
4372 socket_shutdown_complete( socket, ret );
4373 ret = ERROR_SUCCESS;
4377 else ret = send_frame( socket, SOCKET_OPCODE_CLOSE, status, reason, len, TRUE, NULL );
4379 return ret;
4382 DWORD WINAPI WinHttpWebSocketShutdown( HINTERNET hsocket, USHORT status, void *reason, DWORD len )
4384 struct socket *socket;
4385 DWORD ret;
4387 TRACE( "%p, %u, %p, %lu\n", hsocket, status, reason, len );
4389 if ((len && !reason) || len > sizeof(socket->reason)) return ERROR_INVALID_PARAMETER;
4391 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
4392 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
4394 release_object( &socket->hdr );
4395 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
4397 if (socket->state >= SOCKET_STATE_SHUTDOWN)
4399 release_object( &socket->hdr );
4400 return ERROR_INVALID_OPERATION;
4403 ret = send_socket_shutdown( socket, status, reason, len, TRUE );
4404 release_object( &socket->hdr );
4405 return ret;
4408 static DWORD socket_close( struct socket *socket )
4410 BOOL final = FALSE;
4411 DWORD ret, count;
4413 if (socket->close_frame_received) return socket->close_frame_receive_err;
4415 if ((ret = socket_drain( socket ))) return ret;
4417 while (1)
4419 if ((ret = receive_frame( socket, &count, &socket->opcode, &final ))) return ret;
4420 if (socket->opcode == SOCKET_OPCODE_CLOSE) break;
4422 socket->read_size = count;
4423 if ((ret = socket_drain( socket ))) return ret;
4425 if (!final)
4426 FIXME( "Received close opcode without FIN bit.\n" );
4428 return receive_close_status( socket, count );
4431 static void socket_close_complete( struct socket *socket, DWORD ret )
4433 if (!ret) send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE, NULL, 0 );
4434 else
4436 WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
4437 result.AsyncResult.dwResult = API_READ_DATA; /* FIXME */
4438 result.AsyncResult.dwError = ret;
4439 result.Operation = WINHTTP_WEB_SOCKET_CLOSE_OPERATION;
4440 send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
4444 static void task_socket_close( void *ctx, BOOL abort )
4446 struct socket_shutdown *s = ctx;
4447 struct socket *socket = (struct socket *)s->task_hdr.obj;
4448 DWORD ret;
4450 if (abort)
4452 socket_close_complete( socket, ERROR_WINHTTP_OPERATION_CANCELLED );
4453 return;
4456 TRACE("running %p\n", ctx);
4458 ret = socket_close( socket );
4459 receive_io_complete( socket );
4460 if (task_needs_completion( &s->task_hdr ))
4461 socket_close_complete( socket, ret );
4464 DWORD WINAPI WinHttpWebSocketClose( HINTERNET hsocket, USHORT status, void *reason, DWORD len )
4466 enum socket_state prev_state;
4467 LONG pending_receives = 0;
4468 struct socket *socket;
4469 DWORD ret;
4471 TRACE( "%p, %u, %p, %lu\n", hsocket, status, reason, len );
4473 if ((len && !reason) || len > sizeof(socket->reason)) return ERROR_INVALID_PARAMETER;
4475 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
4476 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
4478 release_object( &socket->hdr );
4479 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
4481 if (socket->state >= SOCKET_STATE_CLOSED)
4483 release_object( &socket->hdr );
4484 return ERROR_INVALID_OPERATION;
4487 prev_state = socket->state;
4488 socket->state = SOCKET_STATE_CLOSED;
4490 if (socket->hdr.flags & WINHTTP_FLAG_ASYNC)
4492 pending_receives = InterlockedIncrement( &socket->hdr.pending_receives );
4493 cancel_queue( &socket->recv_q );
4496 if (prev_state < SOCKET_STATE_SHUTDOWN && (ret = send_socket_shutdown( socket, status, reason, len, FALSE )))
4497 goto done;
4499 if (pending_receives == 1 && socket->close_frame_received)
4501 if (socket->hdr.flags & WINHTTP_FLAG_ASYNC)
4502 socket_close_complete( socket, socket->close_frame_receive_err );
4503 goto done;
4506 if (socket->hdr.flags & WINHTTP_FLAG_ASYNC)
4508 struct socket_shutdown *s;
4510 if (!(s = calloc( 1, sizeof(*s) )))
4512 ret = ERROR_OUTOFMEMORY;
4513 goto done;
4515 if ((ret = queue_task( &socket->recv_q, task_socket_close, &s->task_hdr, &socket->hdr )))
4517 InterlockedDecrement( &socket->hdr.pending_receives );
4518 free( s );
4521 else ret = socket_close( socket );
4523 done:
4524 release_object( &socket->hdr );
4525 return ret;
4528 DWORD WINAPI WinHttpWebSocketQueryCloseStatus( HINTERNET hsocket, USHORT *status, void *reason, DWORD len,
4529 DWORD *ret_len )
4531 struct socket *socket;
4532 DWORD ret;
4534 TRACE( "%p, %p, %p, %lu, %p\n", hsocket, status, reason, len, ret_len );
4536 if (!status || (len && !reason) || !ret_len) return ERROR_INVALID_PARAMETER;
4538 if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
4539 if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
4541 release_object( &socket->hdr );
4542 return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
4545 if (!socket->close_frame_received || socket->close_frame_receive_err)
4547 ret = socket->close_frame_received ? socket->close_frame_receive_err : ERROR_INVALID_OPERATION;
4548 release_object( &socket->hdr );
4549 return ret;
4551 *status = socket->status;
4552 *ret_len = socket->reason_len;
4553 if (socket->reason_len > len) ret = ERROR_INSUFFICIENT_BUFFER;
4554 else
4556 memcpy( reason, socket->reason, socket->reason_len );
4557 ret = ERROR_SUCCESS;
4560 release_object( &socket->hdr );
4561 return ret;
4564 enum request_state
4566 REQUEST_STATE_INITIALIZED,
4567 REQUEST_STATE_CANCELLED,
4568 REQUEST_STATE_OPEN,
4569 REQUEST_STATE_SENT,
4570 REQUEST_STATE_RESPONSE_RECEIVED
4573 struct winhttp_request
4575 IWinHttpRequest IWinHttpRequest_iface;
4576 LONG refs;
4577 CRITICAL_SECTION cs;
4578 enum request_state state;
4579 HINTERNET hsession;
4580 HINTERNET hconnect;
4581 HINTERNET hrequest;
4582 VARIANT data;
4583 WCHAR *verb;
4584 HANDLE done;
4585 HANDLE wait;
4586 HANDLE cancel;
4587 BOOL proc_running;
4588 char *buffer;
4589 DWORD offset;
4590 DWORD bytes_available;
4591 DWORD bytes_read;
4592 DWORD error;
4593 DWORD logon_policy;
4594 DWORD disable_feature;
4595 LONG resolve_timeout;
4596 LONG connect_timeout;
4597 LONG send_timeout;
4598 LONG receive_timeout;
4599 WINHTTP_PROXY_INFO proxy;
4600 BOOL async;
4601 UINT url_codepage;
4604 static inline struct winhttp_request *impl_from_IWinHttpRequest( IWinHttpRequest *iface )
4606 return CONTAINING_RECORD( iface, struct winhttp_request, IWinHttpRequest_iface );
4609 static ULONG WINAPI winhttp_request_AddRef(
4610 IWinHttpRequest *iface )
4612 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4613 return InterlockedIncrement( &request->refs );
4616 /* critical section must be held */
4617 static void cancel_request( struct winhttp_request *request )
4619 if (request->state <= REQUEST_STATE_CANCELLED) return;
4621 if (request->proc_running)
4623 SetEvent( request->cancel );
4624 LeaveCriticalSection( &request->cs );
4626 WaitForSingleObject( request->done, INFINITE );
4628 EnterCriticalSection( &request->cs );
4630 request->state = REQUEST_STATE_CANCELLED;
4633 /* critical section must be held */
4634 static void free_request( struct winhttp_request *request )
4636 if (request->state < REQUEST_STATE_INITIALIZED) return;
4637 WinHttpCloseHandle( request->hrequest );
4638 WinHttpCloseHandle( request->hconnect );
4639 WinHttpCloseHandle( request->hsession );
4640 CloseHandle( request->done );
4641 CloseHandle( request->wait );
4642 CloseHandle( request->cancel );
4643 free( request->proxy.lpszProxy );
4644 free( request->proxy.lpszProxyBypass );
4645 free( request->buffer );
4646 free( request->verb );
4647 VariantClear( &request->data );
4650 static ULONG WINAPI winhttp_request_Release(
4651 IWinHttpRequest *iface )
4653 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4654 LONG refs = InterlockedDecrement( &request->refs );
4655 if (!refs)
4657 TRACE("destroying %p\n", request);
4659 EnterCriticalSection( &request->cs );
4660 cancel_request( request );
4661 free_request( request );
4662 LeaveCriticalSection( &request->cs );
4663 request->cs.DebugInfo->Spare[0] = 0;
4664 DeleteCriticalSection( &request->cs );
4665 free( request );
4667 return refs;
4670 static HRESULT WINAPI winhttp_request_QueryInterface(
4671 IWinHttpRequest *iface,
4672 REFIID riid,
4673 void **obj )
4675 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4677 TRACE("%p, %s, %p\n", request, debugstr_guid(riid), obj );
4679 if (IsEqualGUID( riid, &IID_IWinHttpRequest ) ||
4680 IsEqualGUID( riid, &IID_IDispatch ) ||
4681 IsEqualGUID( riid, &IID_IUnknown ))
4683 *obj = iface;
4685 else
4687 FIXME("interface %s not implemented\n", debugstr_guid(riid));
4688 return E_NOINTERFACE;
4690 IWinHttpRequest_AddRef( iface );
4691 return S_OK;
4694 static HRESULT WINAPI winhttp_request_GetTypeInfoCount(
4695 IWinHttpRequest *iface,
4696 UINT *count )
4698 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4700 TRACE("%p, %p\n", request, count);
4701 *count = 1;
4702 return S_OK;
4705 enum type_id
4707 IWinHttpRequest_tid,
4708 last_tid
4711 static ITypeLib *winhttp_typelib;
4712 static ITypeInfo *winhttp_typeinfo[last_tid];
4714 static REFIID winhttp_tid_id[] =
4716 &IID_IWinHttpRequest
4719 static HRESULT get_typeinfo( enum type_id tid, ITypeInfo **ret )
4721 HRESULT hr;
4723 if (!winhttp_typelib)
4725 ITypeLib *typelib;
4727 hr = LoadRegTypeLib( &LIBID_WinHttp, 5, 1, LOCALE_SYSTEM_DEFAULT, &typelib );
4728 if (FAILED(hr))
4730 ERR( "LoadRegTypeLib failed: %#lx\n", hr );
4731 return hr;
4733 if (InterlockedCompareExchangePointer( (void **)&winhttp_typelib, typelib, NULL ))
4734 ITypeLib_Release( typelib );
4736 if (!winhttp_typeinfo[tid])
4738 ITypeInfo *typeinfo;
4740 hr = ITypeLib_GetTypeInfoOfGuid( winhttp_typelib, winhttp_tid_id[tid], &typeinfo );
4741 if (FAILED(hr))
4743 ERR( "GetTypeInfoOfGuid(%s) failed: %#lx\n", debugstr_guid(winhttp_tid_id[tid]), hr );
4744 return hr;
4746 if (InterlockedCompareExchangePointer( (void **)(winhttp_typeinfo + tid), typeinfo, NULL ))
4747 ITypeInfo_Release( typeinfo );
4749 *ret = winhttp_typeinfo[tid];
4750 ITypeInfo_AddRef(winhttp_typeinfo[tid]);
4751 return S_OK;
4754 void release_typelib(void)
4756 unsigned i;
4758 for (i = 0; i < ARRAY_SIZE(winhttp_typeinfo); i++)
4759 if (winhttp_typeinfo[i])
4760 ITypeInfo_Release(winhttp_typeinfo[i]);
4762 if (winhttp_typelib)
4763 ITypeLib_Release(winhttp_typelib);
4766 static HRESULT WINAPI winhttp_request_GetTypeInfo(
4767 IWinHttpRequest *iface,
4768 UINT index,
4769 LCID lcid,
4770 ITypeInfo **info )
4772 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4773 TRACE( "%p, %u, %lu, %p\n", request, index, lcid, info );
4775 return get_typeinfo( IWinHttpRequest_tid, info );
4778 static HRESULT WINAPI winhttp_request_GetIDsOfNames(
4779 IWinHttpRequest *iface,
4780 REFIID riid,
4781 LPOLESTR *names,
4782 UINT count,
4783 LCID lcid,
4784 DISPID *dispid )
4786 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4787 ITypeInfo *typeinfo;
4788 HRESULT hr;
4790 TRACE( "%p, %s, %p, %u, %lu, %p\n", request, debugstr_guid(riid), names, count, lcid, dispid );
4792 if (!names || !count || !dispid) return E_INVALIDARG;
4794 hr = get_typeinfo( IWinHttpRequest_tid, &typeinfo );
4795 if (SUCCEEDED(hr))
4797 hr = ITypeInfo_GetIDsOfNames( typeinfo, names, count, dispid );
4798 ITypeInfo_Release( typeinfo );
4800 return hr;
4803 static HRESULT WINAPI winhttp_request_Invoke(
4804 IWinHttpRequest *iface,
4805 DISPID member,
4806 REFIID riid,
4807 LCID lcid,
4808 WORD flags,
4809 DISPPARAMS *params,
4810 VARIANT *result,
4811 EXCEPINFO *excep_info,
4812 UINT *arg_err )
4814 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4815 ITypeInfo *typeinfo;
4816 HRESULT hr;
4818 TRACE( "%p, %ld, %s, %lu, %d, %p, %p, %p, %p\n", request, member, debugstr_guid(riid),
4819 lcid, flags, params, result, excep_info, arg_err );
4821 if (!IsEqualIID( riid, &IID_NULL )) return DISP_E_UNKNOWNINTERFACE;
4823 if (member == DISPID_HTTPREQUEST_OPTION)
4825 VARIANT ret_value, option;
4826 UINT err_pos;
4828 if (!result) result = &ret_value;
4829 if (!arg_err) arg_err = &err_pos;
4831 VariantInit( &option );
4832 VariantInit( result );
4834 if (!flags) return S_OK;
4836 if (flags == DISPATCH_PROPERTYPUT)
4838 hr = DispGetParam( params, 0, VT_I4, &option, arg_err );
4839 if (FAILED(hr)) return hr;
4841 hr = IWinHttpRequest_put_Option( &request->IWinHttpRequest_iface, V_I4( &option ), params->rgvarg[0] );
4842 if (FAILED(hr))
4843 WARN( "put_Option(%ld) failed: %#lx\n", V_I4( &option ), hr );
4844 return hr;
4846 else if (flags & (DISPATCH_PROPERTYGET | DISPATCH_METHOD))
4848 hr = DispGetParam( params, 0, VT_I4, &option, arg_err );
4849 if (FAILED(hr)) return hr;
4851 hr = IWinHttpRequest_get_Option( &request->IWinHttpRequest_iface, V_I4( &option ), result );
4852 if (FAILED(hr))
4853 WARN( "get_Option(%ld) failed: %#lx\n", V_I4( &option ), hr );
4854 return hr;
4857 FIXME("unsupported flags %x\n", flags);
4858 return E_NOTIMPL;
4861 /* fallback to standard implementation */
4863 hr = get_typeinfo( IWinHttpRequest_tid, &typeinfo );
4864 if (SUCCEEDED(hr))
4866 hr = ITypeInfo_Invoke( typeinfo, &request->IWinHttpRequest_iface, member, flags,
4867 params, result, excep_info, arg_err );
4868 ITypeInfo_Release( typeinfo );
4870 return hr;
4873 static HRESULT WINAPI winhttp_request_SetProxy(
4874 IWinHttpRequest *iface,
4875 HTTPREQUEST_PROXY_SETTING proxy_setting,
4876 VARIANT proxy_server,
4877 VARIANT bypass_list )
4879 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4880 DWORD err = ERROR_SUCCESS;
4882 TRACE( "%p, %lu, %s, %s\n", request, proxy_setting, debugstr_variant(&proxy_server),
4883 debugstr_variant(&bypass_list) );
4885 EnterCriticalSection( &request->cs );
4886 switch (proxy_setting)
4888 case HTTPREQUEST_PROXYSETTING_DEFAULT:
4889 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
4890 free( request->proxy.lpszProxy );
4891 free( request->proxy.lpszProxyBypass );
4892 request->proxy.lpszProxy = NULL;
4893 request->proxy.lpszProxyBypass = NULL;
4894 break;
4896 case HTTPREQUEST_PROXYSETTING_DIRECT:
4897 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NO_PROXY;
4898 free( request->proxy.lpszProxy );
4899 free( request->proxy.lpszProxyBypass );
4900 request->proxy.lpszProxy = NULL;
4901 request->proxy.lpszProxyBypass = NULL;
4902 break;
4904 case HTTPREQUEST_PROXYSETTING_PROXY:
4905 request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
4906 if (V_VT( &proxy_server ) == VT_BSTR)
4908 free( request->proxy.lpszProxy );
4909 request->proxy.lpszProxy = wcsdup( V_BSTR( &proxy_server ) );
4911 if (V_VT( &bypass_list ) == VT_BSTR)
4913 free( request->proxy.lpszProxyBypass );
4914 request->proxy.lpszProxyBypass = wcsdup( V_BSTR( &bypass_list ) );
4916 break;
4918 default:
4919 err = ERROR_INVALID_PARAMETER;
4920 break;
4922 LeaveCriticalSection( &request->cs );
4923 return HRESULT_FROM_WIN32( err );
4926 static HRESULT WINAPI winhttp_request_SetCredentials(
4927 IWinHttpRequest *iface,
4928 BSTR username,
4929 BSTR password,
4930 HTTPREQUEST_SETCREDENTIALS_FLAGS flags )
4932 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
4933 DWORD target, scheme = WINHTTP_AUTH_SCHEME_BASIC; /* FIXME: query supported schemes */
4934 DWORD err = ERROR_SUCCESS;
4936 TRACE( "%p, %s, %p, %#lx\n", request, debugstr_w(username), password, flags );
4938 EnterCriticalSection( &request->cs );
4939 if (request->state < REQUEST_STATE_OPEN)
4941 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN;
4942 goto done;
4944 switch (flags)
4946 case HTTPREQUEST_SETCREDENTIALS_FOR_SERVER:
4947 target = WINHTTP_AUTH_TARGET_SERVER;
4948 break;
4949 case HTTPREQUEST_SETCREDENTIALS_FOR_PROXY:
4950 target = WINHTTP_AUTH_TARGET_PROXY;
4951 break;
4952 default:
4953 err = ERROR_INVALID_PARAMETER;
4954 goto done;
4956 if (!WinHttpSetCredentials( request->hrequest, target, scheme, username, password, NULL ))
4958 err = GetLastError();
4960 done:
4961 LeaveCriticalSection( &request->cs );
4962 return HRESULT_FROM_WIN32( err );
4965 static void initialize_request( struct winhttp_request *request )
4967 request->wait = CreateEventW( NULL, FALSE, FALSE, NULL );
4968 request->cancel = CreateEventW( NULL, FALSE, FALSE, NULL );
4969 request->done = CreateEventW( NULL, FALSE, FALSE, NULL );
4970 request->connect_timeout = 60000;
4971 request->send_timeout = 30000;
4972 request->receive_timeout = 30000;
4973 request->url_codepage = CP_UTF8;
4974 VariantInit( &request->data );
4975 request->state = REQUEST_STATE_INITIALIZED;
4978 static void reset_request( struct winhttp_request *request )
4980 cancel_request( request );
4981 WinHttpCloseHandle( request->hrequest );
4982 request->hrequest = NULL;
4983 WinHttpCloseHandle( request->hconnect );
4984 request->hconnect = NULL;
4985 free( request->buffer );
4986 request->buffer = NULL;
4987 free( request->verb );
4988 request->verb = NULL;
4989 request->offset = 0;
4990 request->bytes_available = 0;
4991 request->bytes_read = 0;
4992 request->error = ERROR_SUCCESS;
4993 request->logon_policy = 0;
4994 request->disable_feature = 0;
4995 request->async = FALSE;
4996 request->connect_timeout = 60000;
4997 request->send_timeout = 30000;
4998 request->receive_timeout = 30000;
4999 request->url_codepage = CP_UTF8;
5000 free( request->proxy.lpszProxy );
5001 request->proxy.lpszProxy = NULL;
5002 free( request->proxy.lpszProxyBypass );
5003 request->proxy.lpszProxyBypass = NULL;
5004 VariantClear( &request->data );
5005 request->state = REQUEST_STATE_INITIALIZED;
5008 static HRESULT WINAPI winhttp_request_Open(
5009 IWinHttpRequest *iface,
5010 BSTR method,
5011 BSTR url,
5012 VARIANT async )
5014 static const WCHAR httpsW[] = {'h','t','t','p','s'};
5015 static const WCHAR *acceptW[] = {L"*/*", NULL};
5016 static const WCHAR user_agentW[] = L"Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)";
5017 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5018 URL_COMPONENTS uc;
5019 WCHAR *hostname, *path = NULL, *verb = NULL;
5020 DWORD err = ERROR_OUTOFMEMORY, len, flags = 0;
5022 TRACE("%p, %s, %s, %s\n", request, debugstr_w(method), debugstr_w(url),
5023 debugstr_variant(&async));
5025 if (!method || !url) return E_INVALIDARG;
5027 memset( &uc, 0, sizeof(uc) );
5028 uc.dwStructSize = sizeof(uc);
5029 uc.dwSchemeLength = ~0u;
5030 uc.dwHostNameLength = ~0u;
5031 uc.dwUrlPathLength = ~0u;
5032 uc.dwExtraInfoLength = ~0u;
5033 if (!WinHttpCrackUrl( url, 0, 0, &uc )) return HRESULT_FROM_WIN32( GetLastError() );
5035 EnterCriticalSection( &request->cs );
5036 reset_request( request );
5038 if (!(hostname = malloc( (uc.dwHostNameLength + 1) * sizeof(WCHAR) ))) goto error;
5039 memcpy( hostname, uc.lpszHostName, uc.dwHostNameLength * sizeof(WCHAR) );
5040 hostname[uc.dwHostNameLength] = 0;
5042 if (!(path = malloc( (uc.dwUrlPathLength + uc.dwExtraInfoLength + 1) * sizeof(WCHAR) ))) goto error;
5043 memcpy( path, uc.lpszUrlPath, (uc.dwUrlPathLength + uc.dwExtraInfoLength) * sizeof(WCHAR) );
5044 path[uc.dwUrlPathLength + uc.dwExtraInfoLength] = 0;
5046 if (!(verb = wcsdup( method ))) goto error;
5047 if (SUCCEEDED( VariantChangeType( &async, &async, 0, VT_BOOL )) && V_BOOL( &async )) request->async = TRUE;
5048 else request->async = FALSE;
5050 if (!request->hsession)
5052 if (!(request->hsession = WinHttpOpen( user_agentW, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, NULL, NULL,
5053 WINHTTP_FLAG_ASYNC )))
5055 err = GetLastError();
5056 goto error;
5058 if (!(request->hconnect = WinHttpConnect( request->hsession, hostname, uc.nPort, 0 )))
5060 WinHttpCloseHandle( request->hsession );
5061 request->hsession = NULL;
5062 err = GetLastError();
5063 goto error;
5066 else if (!(request->hconnect = WinHttpConnect( request->hsession, hostname, uc.nPort, 0 )))
5068 err = GetLastError();
5069 goto error;
5072 len = ARRAY_SIZE( httpsW );
5073 if (uc.dwSchemeLength == len && !memcmp( uc.lpszScheme, httpsW, len * sizeof(WCHAR) ))
5075 flags |= WINHTTP_FLAG_SECURE;
5077 if (!(request->hrequest = WinHttpOpenRequest( request->hconnect, method, path, NULL, NULL, acceptW, flags )))
5079 err = GetLastError();
5080 goto error;
5082 WinHttpSetOption( request->hrequest, WINHTTP_OPTION_CONTEXT_VALUE, &request, sizeof(request) );
5084 request->state = REQUEST_STATE_OPEN;
5085 request->verb = verb;
5086 free( hostname );
5087 free( path );
5088 LeaveCriticalSection( &request->cs );
5089 return S_OK;
5091 error:
5092 WinHttpCloseHandle( request->hconnect );
5093 request->hconnect = NULL;
5094 free( hostname );
5095 free( path );
5096 free( verb );
5097 LeaveCriticalSection( &request->cs );
5098 return HRESULT_FROM_WIN32( err );
5101 static HRESULT WINAPI winhttp_request_SetRequestHeader(
5102 IWinHttpRequest *iface,
5103 BSTR header,
5104 BSTR value )
5106 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5107 DWORD len, err = ERROR_SUCCESS;
5108 WCHAR *str;
5110 TRACE("%p, %s, %s\n", request, debugstr_w(header), debugstr_w(value));
5112 if (!header) return E_INVALIDARG;
5114 EnterCriticalSection( &request->cs );
5115 if (request->state < REQUEST_STATE_OPEN)
5117 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN;
5118 goto done;
5120 if (request->state >= REQUEST_STATE_SENT)
5122 err = ERROR_WINHTTP_CANNOT_CALL_AFTER_SEND;
5123 goto done;
5125 len = lstrlenW( header ) + 4;
5126 if (value) len += lstrlenW( value );
5127 if (!(str = malloc( (len + 1) * sizeof(WCHAR) )))
5129 err = ERROR_OUTOFMEMORY;
5130 goto done;
5132 swprintf( str, len + 1, L"%s: %s\r\n", header, value ? value : L"" );
5133 if (!WinHttpAddRequestHeaders( request->hrequest, str, len,
5134 WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE ))
5136 err = GetLastError();
5138 free( str );
5140 done:
5141 LeaveCriticalSection( &request->cs );
5142 return HRESULT_FROM_WIN32( err );
5145 static HRESULT WINAPI winhttp_request_GetResponseHeader(
5146 IWinHttpRequest *iface,
5147 BSTR header,
5148 BSTR *value )
5150 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5151 DWORD size, err = ERROR_SUCCESS;
5153 TRACE("%p, %p\n", request, header);
5155 EnterCriticalSection( &request->cs );
5156 if (request->state < REQUEST_STATE_SENT)
5158 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5159 goto done;
5161 if (!header || !value)
5163 err = ERROR_INVALID_PARAMETER;
5164 goto done;
5166 size = 0;
5167 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CUSTOM, header, NULL, &size, NULL ))
5169 err = GetLastError();
5170 if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
5172 if (!(*value = SysAllocStringLen( NULL, size / sizeof(WCHAR) )))
5174 err = ERROR_OUTOFMEMORY;
5175 goto done;
5177 err = ERROR_SUCCESS;
5178 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CUSTOM, header, *value, &size, NULL ))
5180 err = GetLastError();
5181 SysFreeString( *value );
5183 done:
5184 LeaveCriticalSection( &request->cs );
5185 return HRESULT_FROM_WIN32( err );
5188 static HRESULT WINAPI winhttp_request_GetAllResponseHeaders(
5189 IWinHttpRequest *iface,
5190 BSTR *headers )
5192 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5193 DWORD size, err = ERROR_SUCCESS;
5195 TRACE("%p, %p\n", request, headers);
5197 if (!headers) return E_INVALIDARG;
5199 EnterCriticalSection( &request->cs );
5200 if (request->state < REQUEST_STATE_SENT)
5202 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5203 goto done;
5205 size = 0;
5206 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, NULL, &size, NULL ))
5208 err = GetLastError();
5209 if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
5211 if (!(*headers = SysAllocStringLen( NULL, size / sizeof(WCHAR) )))
5213 err = ERROR_OUTOFMEMORY;
5214 goto done;
5216 err = ERROR_SUCCESS;
5217 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, *headers, &size, NULL ))
5219 err = GetLastError();
5220 SysFreeString( *headers );
5222 done:
5223 LeaveCriticalSection( &request->cs );
5224 return HRESULT_FROM_WIN32( err );
5227 static void CALLBACK wait_status_callback( HINTERNET handle, DWORD_PTR context, DWORD status, LPVOID buffer, DWORD size )
5229 struct winhttp_request *request = (struct winhttp_request *)context;
5231 switch (status)
5233 case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
5234 request->bytes_available = *(DWORD *)buffer;
5235 request->error = ERROR_SUCCESS;
5236 break;
5237 case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
5238 request->bytes_read = size;
5239 request->error = ERROR_SUCCESS;
5240 break;
5241 case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
5243 WINHTTP_ASYNC_RESULT *result = (WINHTTP_ASYNC_RESULT *)buffer;
5244 request->error = result->dwError;
5245 break;
5247 default:
5248 request->error = ERROR_SUCCESS;
5249 break;
5251 SetEvent( request->wait );
5254 static void wait_set_status_callback( struct winhttp_request *request, DWORD status )
5256 status |= WINHTTP_CALLBACK_STATUS_REQUEST_ERROR;
5257 WinHttpSetStatusCallback( request->hrequest, wait_status_callback, status, 0 );
5260 static DWORD wait_for_completion( struct winhttp_request *request )
5262 HANDLE handles[2] = { request->wait, request->cancel };
5263 DWORD ret;
5265 switch (WaitForMultipleObjects( 2, handles, FALSE, INFINITE ))
5267 case WAIT_OBJECT_0:
5268 ret = request->error;
5269 break;
5270 case WAIT_OBJECT_0 + 1:
5271 ret = request->error = ERROR_CANCELLED;
5272 SetEvent( request->done );
5273 break;
5274 default:
5275 ret = request->error = GetLastError();
5276 break;
5278 return ret;
5281 static HRESULT request_receive( struct winhttp_request *request )
5283 DWORD err, size, buflen = 4096;
5285 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE );
5286 if (!WinHttpReceiveResponse( request->hrequest, NULL ))
5288 return HRESULT_FROM_WIN32( GetLastError() );
5290 if ((err = wait_for_completion( request ))) return HRESULT_FROM_WIN32( err );
5291 if (!wcscmp( request->verb, L"HEAD" ))
5293 request->state = REQUEST_STATE_RESPONSE_RECEIVED;
5294 return S_OK;
5296 if (!(request->buffer = malloc( buflen ))) return E_OUTOFMEMORY;
5297 request->buffer[0] = 0;
5298 size = 0;
5301 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE );
5302 if (!WinHttpQueryDataAvailable( request->hrequest, &request->bytes_available ))
5304 err = GetLastError();
5305 goto error;
5307 if ((err = wait_for_completion( request ))) goto error;
5308 if (!request->bytes_available) break;
5309 size += request->bytes_available;
5310 if (buflen < size)
5312 char *tmp;
5313 while (buflen < size) buflen *= 2;
5314 if (!(tmp = realloc( request->buffer, buflen )))
5316 err = ERROR_OUTOFMEMORY;
5317 goto error;
5319 request->buffer = tmp;
5321 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_READ_COMPLETE );
5322 if (!WinHttpReadData( request->hrequest, request->buffer + request->offset,
5323 request->bytes_available, &request->bytes_read ))
5325 err = GetLastError();
5326 goto error;
5328 if ((err = wait_for_completion( request ))) goto error;
5329 request->offset += request->bytes_read;
5330 } while (request->bytes_read);
5332 request->state = REQUEST_STATE_RESPONSE_RECEIVED;
5333 return S_OK;
5335 error:
5336 free( request->buffer );
5337 request->buffer = NULL;
5338 return HRESULT_FROM_WIN32( err );
5341 static DWORD request_set_parameters( struct winhttp_request *request )
5343 if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_PROXY, &request->proxy,
5344 sizeof(request->proxy) )) return GetLastError();
5346 if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_AUTOLOGON_POLICY, &request->logon_policy,
5347 sizeof(request->logon_policy) )) return GetLastError();
5349 if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_DISABLE_FEATURE, &request->disable_feature,
5350 sizeof(request->disable_feature) )) return GetLastError();
5352 if (!WinHttpSetTimeouts( request->hrequest,
5353 request->resolve_timeout,
5354 request->connect_timeout,
5355 request->send_timeout,
5356 request->receive_timeout )) return GetLastError();
5357 return ERROR_SUCCESS;
5360 static void request_set_utf8_content_type( struct winhttp_request *request )
5362 WCHAR headerW[64];
5363 int len;
5365 len = swprintf( headerW, ARRAY_SIZE(headerW), L"%s: %s", L"Content-Type", L"text/plain" );
5366 WinHttpAddRequestHeaders( request->hrequest, headerW, len, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW );
5368 len = swprintf( headerW, ARRAY_SIZE(headerW), L"%s: %s", L"Content-Type", L"charset=utf-8" );
5369 WinHttpAddRequestHeaders( request->hrequest, headerW, len, WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON );
5372 static HRESULT request_send( struct winhttp_request *request )
5374 SAFEARRAY *sa = NULL;
5375 VARIANT data;
5376 char *ptr = NULL;
5377 LONG size = 0;
5378 HRESULT hr;
5379 DWORD err;
5381 if ((err = request_set_parameters( request ))) return HRESULT_FROM_WIN32( err );
5382 if (wcscmp( request->verb, L"GET" ))
5384 VariantInit( &data );
5385 if (V_VT( &request->data ) == VT_BSTR)
5387 UINT cp = CP_ACP;
5388 const WCHAR *str = V_BSTR( &request->data );
5389 int i, len = lstrlenW( str );
5391 for (i = 0; i < len; i++)
5393 if (str[i] > 127)
5395 cp = CP_UTF8;
5396 break;
5399 size = WideCharToMultiByte( cp, 0, str, len, NULL, 0, NULL, NULL );
5400 if (!(ptr = malloc( size ))) return E_OUTOFMEMORY;
5401 WideCharToMultiByte( cp, 0, str, len, ptr, size, NULL, NULL );
5402 if (cp == CP_UTF8) request_set_utf8_content_type( request );
5404 else if (VariantChangeType( &data, &request->data, 0, VT_ARRAY|VT_UI1 ) == S_OK)
5406 sa = V_ARRAY( &data );
5407 if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK) return hr;
5408 if ((hr = SafeArrayGetUBound( sa, 1, &size )) != S_OK)
5410 SafeArrayUnaccessData( sa );
5411 return hr;
5413 size++;
5416 wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE );
5417 if (!WinHttpSendRequest( request->hrequest, NULL, 0, ptr, size, size, 0 ))
5419 err = GetLastError();
5420 goto error;
5422 if ((err = wait_for_completion( request ))) goto error;
5423 if (sa) SafeArrayUnaccessData( sa );
5424 else free( ptr );
5425 request->state = REQUEST_STATE_SENT;
5426 return S_OK;
5428 error:
5429 if (sa) SafeArrayUnaccessData( sa );
5430 else free( ptr );
5431 return HRESULT_FROM_WIN32( err );
5434 static void CALLBACK send_and_receive_proc( TP_CALLBACK_INSTANCE *instance, void *ctx )
5436 struct winhttp_request *request = (struct winhttp_request *)ctx;
5437 if (request_send( request ) == S_OK) request_receive( request );
5438 SetEvent( request->done );
5441 /* critical section must be held */
5442 static DWORD request_wait( struct winhttp_request *request, DWORD timeout )
5444 HANDLE done = request->done;
5445 DWORD err, ret;
5447 LeaveCriticalSection( &request->cs );
5448 while ((err = MsgWaitForMultipleObjects( 1, &done, FALSE, timeout, QS_ALLINPUT )) == WAIT_OBJECT_0 + 1)
5450 MSG msg;
5451 while (PeekMessageW( &msg, NULL, 0, 0, PM_REMOVE ))
5453 TranslateMessage( &msg );
5454 DispatchMessageW( &msg );
5457 switch (err)
5459 case WAIT_OBJECT_0:
5460 ret = request->error;
5461 break;
5462 case WAIT_TIMEOUT:
5463 ret = ERROR_TIMEOUT;
5464 break;
5465 default:
5466 ret = GetLastError();
5467 break;
5469 EnterCriticalSection( &request->cs );
5470 if (err == WAIT_OBJECT_0) request->proc_running = FALSE;
5471 return ret;
5474 static HRESULT WINAPI winhttp_request_Send(
5475 IWinHttpRequest *iface,
5476 VARIANT body )
5478 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5479 HRESULT hr;
5481 TRACE("%p, %s\n", request, debugstr_variant(&body));
5483 EnterCriticalSection( &request->cs );
5484 if (request->state < REQUEST_STATE_OPEN)
5486 LeaveCriticalSection( &request->cs );
5487 return HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN );
5489 if (request->state >= REQUEST_STATE_SENT)
5491 LeaveCriticalSection( &request->cs );
5492 return S_OK;
5494 VariantClear( &request->data );
5495 if ((hr = VariantCopyInd( &request->data, &body )) != S_OK)
5497 LeaveCriticalSection( &request->cs );
5498 return hr;
5500 if (!TrySubmitThreadpoolCallback( send_and_receive_proc, request, NULL ))
5502 LeaveCriticalSection( &request->cs );
5503 return HRESULT_FROM_WIN32( GetLastError() );
5505 request->proc_running = TRUE;
5506 if (!request->async)
5508 hr = HRESULT_FROM_WIN32( request_wait( request, INFINITE ) );
5510 LeaveCriticalSection( &request->cs );
5511 return hr;
5514 static HRESULT WINAPI winhttp_request_get_Status(
5515 IWinHttpRequest *iface,
5516 LONG *status )
5518 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5519 DWORD err = ERROR_SUCCESS, flags, status_code, len = sizeof(status_code), index = 0;
5521 TRACE("%p, %p\n", request, status);
5523 if (!status) return E_INVALIDARG;
5525 EnterCriticalSection( &request->cs );
5526 if (request->state < REQUEST_STATE_SENT)
5528 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5529 goto done;
5531 flags = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER;
5532 if (!WinHttpQueryHeaders( request->hrequest, flags, NULL, &status_code, &len, &index ))
5534 err = GetLastError();
5535 goto done;
5537 *status = status_code;
5539 done:
5540 LeaveCriticalSection( &request->cs );
5541 return HRESULT_FROM_WIN32( err );
5544 static HRESULT WINAPI winhttp_request_get_StatusText(
5545 IWinHttpRequest *iface,
5546 BSTR *status )
5548 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5549 DWORD err = ERROR_SUCCESS, len = 0, index = 0;
5551 TRACE("%p, %p\n", request, status);
5553 if (!status) return E_INVALIDARG;
5555 EnterCriticalSection( &request->cs );
5556 if (request->state < REQUEST_STATE_SENT)
5558 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5559 goto done;
5561 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_STATUS_TEXT, NULL, NULL, &len, &index ))
5563 err = GetLastError();
5564 if (err != ERROR_INSUFFICIENT_BUFFER) goto done;
5566 if (!(*status = SysAllocStringLen( NULL, len / sizeof(WCHAR) )))
5568 err = ERROR_OUTOFMEMORY;
5569 goto done;
5571 index = 0;
5572 err = ERROR_SUCCESS;
5573 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_STATUS_TEXT, NULL, *status, &len, &index ))
5575 err = GetLastError();
5576 SysFreeString( *status );
5578 done:
5579 LeaveCriticalSection( &request->cs );
5580 return HRESULT_FROM_WIN32( err );
5583 static DWORD request_get_codepage( struct winhttp_request *request, UINT *codepage )
5585 WCHAR *buffer, *p;
5586 DWORD size;
5588 *codepage = CP_ACP;
5589 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, NULL, &size, NULL ) &&
5590 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
5592 if (!(buffer = malloc( size ))) return ERROR_OUTOFMEMORY;
5593 if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, buffer, &size, NULL ))
5595 free( buffer );
5596 return GetLastError();
5598 if ((p = wcsstr( buffer, L"charset" )))
5600 p += lstrlenW( L"charset" );
5601 while (*p == ' ') p++;
5602 if (*p++ == '=')
5604 while (*p == ' ') p++;
5605 if (!wcsicmp( p, L"utf-8" )) *codepage = CP_UTF8;
5608 free( buffer );
5610 return ERROR_SUCCESS;
5613 static HRESULT WINAPI winhttp_request_get_ResponseText(
5614 IWinHttpRequest *iface,
5615 BSTR *body )
5617 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5618 UINT codepage;
5619 DWORD err = ERROR_SUCCESS;
5620 int len;
5622 TRACE("%p, %p\n", request, body);
5624 if (!body) return E_INVALIDARG;
5626 EnterCriticalSection( &request->cs );
5627 if (request->state < REQUEST_STATE_SENT)
5629 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5630 goto done;
5632 if ((err = request_get_codepage( request, &codepage ))) goto done;
5633 len = MultiByteToWideChar( codepage, 0, request->buffer, request->offset, NULL, 0 );
5634 if (!(*body = SysAllocStringLen( NULL, len )))
5636 err = ERROR_OUTOFMEMORY;
5637 goto done;
5639 MultiByteToWideChar( codepage, 0, request->buffer, request->offset, *body, len );
5640 (*body)[len] = 0;
5642 done:
5643 LeaveCriticalSection( &request->cs );
5644 return HRESULT_FROM_WIN32( err );
5647 static HRESULT WINAPI winhttp_request_get_ResponseBody(
5648 IWinHttpRequest *iface,
5649 VARIANT *body )
5651 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5652 SAFEARRAY *sa;
5653 HRESULT hr;
5654 DWORD err = ERROR_SUCCESS;
5655 char *ptr;
5657 TRACE("%p, %p\n", request, body);
5659 if (!body) return E_INVALIDARG;
5661 EnterCriticalSection( &request->cs );
5662 if (request->state < REQUEST_STATE_SENT)
5664 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5665 goto done;
5667 if (!(sa = SafeArrayCreateVector( VT_UI1, 0, request->offset )))
5669 err = ERROR_OUTOFMEMORY;
5670 goto done;
5672 if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK)
5674 SafeArrayDestroy( sa );
5675 LeaveCriticalSection( &request->cs );
5676 return hr;
5678 memcpy( ptr, request->buffer, request->offset );
5679 if ((hr = SafeArrayUnaccessData( sa )) != S_OK)
5681 SafeArrayDestroy( sa );
5682 LeaveCriticalSection( &request->cs );
5683 return hr;
5685 V_VT( body ) = VT_ARRAY|VT_UI1;
5686 V_ARRAY( body ) = sa;
5688 done:
5689 LeaveCriticalSection( &request->cs );
5690 return HRESULT_FROM_WIN32( err );
5693 struct stream
5695 IStream IStream_iface;
5696 LONG refs;
5697 char *data;
5698 ULARGE_INTEGER pos, size;
5701 static inline struct stream *impl_from_IStream( IStream *iface )
5703 return CONTAINING_RECORD( iface, struct stream, IStream_iface );
5706 static HRESULT WINAPI stream_QueryInterface( IStream *iface, REFIID riid, void **obj )
5708 struct stream *stream = impl_from_IStream( iface );
5710 TRACE("%p, %s, %p\n", stream, debugstr_guid(riid), obj);
5712 if (IsEqualGUID( riid, &IID_IStream ) || IsEqualGUID( riid, &IID_IUnknown ))
5714 *obj = iface;
5716 else
5718 FIXME("interface %s not implemented\n", debugstr_guid(riid));
5719 return E_NOINTERFACE;
5721 IStream_AddRef( iface );
5722 return S_OK;
5725 static ULONG WINAPI stream_AddRef( IStream *iface )
5727 struct stream *stream = impl_from_IStream( iface );
5728 return InterlockedIncrement( &stream->refs );
5731 static ULONG WINAPI stream_Release( IStream *iface )
5733 struct stream *stream = impl_from_IStream( iface );
5734 LONG refs = InterlockedDecrement( &stream->refs );
5735 if (!refs)
5737 free( stream->data );
5738 free( stream );
5740 return refs;
5743 static HRESULT WINAPI stream_Read( IStream *iface, void *buf, ULONG len, ULONG *read )
5745 struct stream *stream = impl_from_IStream( iface );
5746 ULONG size;
5748 if (stream->pos.QuadPart >= stream->size.QuadPart)
5750 *read = 0;
5751 return S_FALSE;
5754 size = min( stream->size.QuadPart - stream->pos.QuadPart, len );
5755 memcpy( buf, stream->data + stream->pos.QuadPart, size );
5756 stream->pos.QuadPart += size;
5757 *read = size;
5759 return S_OK;
5762 static HRESULT WINAPI stream_Write( IStream *iface, const void *buf, ULONG len, ULONG *written )
5764 FIXME("\n");
5765 return E_NOTIMPL;
5768 static HRESULT WINAPI stream_Seek( IStream *iface, LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *newpos )
5770 struct stream *stream = impl_from_IStream( iface );
5772 if (origin == STREAM_SEEK_SET)
5773 stream->pos.QuadPart = move.QuadPart;
5774 else if (origin == STREAM_SEEK_CUR)
5775 stream->pos.QuadPart += move.QuadPart;
5776 else if (origin == STREAM_SEEK_END)
5777 stream->pos.QuadPart = stream->size.QuadPart - move.QuadPart;
5779 if (newpos) newpos->QuadPart = stream->pos.QuadPart;
5780 return S_OK;
5783 static HRESULT WINAPI stream_SetSize( IStream *iface, ULARGE_INTEGER newsize )
5785 FIXME("\n");
5786 return E_NOTIMPL;
5789 static HRESULT WINAPI stream_CopyTo( IStream *iface, IStream *stream, ULARGE_INTEGER len, ULARGE_INTEGER *read,
5790 ULARGE_INTEGER *written )
5792 FIXME("\n");
5793 return E_NOTIMPL;
5796 static HRESULT WINAPI stream_Commit( IStream *iface, DWORD flags )
5798 FIXME("\n");
5799 return E_NOTIMPL;
5802 static HRESULT WINAPI stream_Revert( IStream *iface )
5804 FIXME("\n");
5805 return E_NOTIMPL;
5808 static HRESULT WINAPI stream_LockRegion( IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER len, DWORD locktype )
5810 FIXME("\n");
5811 return E_NOTIMPL;
5814 static HRESULT WINAPI stream_UnlockRegion( IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER len, DWORD locktype )
5816 FIXME("\n");
5817 return E_NOTIMPL;
5820 static HRESULT WINAPI stream_Stat( IStream *iface, STATSTG *stg, DWORD flag )
5822 FIXME("\n");
5823 return E_NOTIMPL;
5826 static HRESULT WINAPI stream_Clone( IStream *iface, IStream **stream )
5828 FIXME("\n");
5829 return E_NOTIMPL;
5832 static const IStreamVtbl stream_vtbl =
5834 stream_QueryInterface,
5835 stream_AddRef,
5836 stream_Release,
5837 stream_Read,
5838 stream_Write,
5839 stream_Seek,
5840 stream_SetSize,
5841 stream_CopyTo,
5842 stream_Commit,
5843 stream_Revert,
5844 stream_LockRegion,
5845 stream_UnlockRegion,
5846 stream_Stat,
5847 stream_Clone
5850 static HRESULT WINAPI winhttp_request_get_ResponseStream(
5851 IWinHttpRequest *iface,
5852 VARIANT *body )
5854 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5855 DWORD err = ERROR_SUCCESS;
5856 struct stream *stream;
5858 TRACE("%p, %p\n", request, body);
5860 if (!body) return E_INVALIDARG;
5862 EnterCriticalSection( &request->cs );
5863 if (request->state < REQUEST_STATE_SENT)
5865 err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND;
5866 goto done;
5868 if (!(stream = malloc( sizeof(*stream) )))
5870 err = ERROR_OUTOFMEMORY;
5871 goto done;
5873 stream->IStream_iface.lpVtbl = &stream_vtbl;
5874 stream->refs = 1;
5875 if (!(stream->data = malloc( request->offset )))
5877 free( stream );
5878 err = ERROR_OUTOFMEMORY;
5879 goto done;
5881 memcpy( stream->data, request->buffer, request->offset );
5882 stream->pos.QuadPart = 0;
5883 stream->size.QuadPart = request->offset;
5884 V_VT( body ) = VT_UNKNOWN;
5885 V_UNKNOWN( body ) = (IUnknown *)&stream->IStream_iface;
5887 done:
5888 LeaveCriticalSection( &request->cs );
5889 return HRESULT_FROM_WIN32( err );
5892 static HRESULT WINAPI winhttp_request_get_Option(
5893 IWinHttpRequest *iface,
5894 WinHttpRequestOption option,
5895 VARIANT *value )
5897 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5898 HRESULT hr = S_OK;
5900 TRACE("%p, %u, %p\n", request, option, value);
5902 EnterCriticalSection( &request->cs );
5903 switch (option)
5905 case WinHttpRequestOption_URLCodePage:
5906 V_VT( value ) = VT_I4;
5907 V_I4( value ) = request->url_codepage;
5908 break;
5909 default:
5910 FIXME("unimplemented option %u\n", option);
5911 hr = E_NOTIMPL;
5912 break;
5914 LeaveCriticalSection( &request->cs );
5915 return hr;
5918 static HRESULT WINAPI winhttp_request_put_Option(
5919 IWinHttpRequest *iface,
5920 WinHttpRequestOption option,
5921 VARIANT value )
5923 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5924 HRESULT hr = S_OK;
5926 TRACE("%p, %u, %s\n", request, option, debugstr_variant(&value));
5928 EnterCriticalSection( &request->cs );
5929 switch (option)
5931 case WinHttpRequestOption_EnableRedirects:
5933 if (V_BOOL( &value ))
5934 request->disable_feature &= ~WINHTTP_DISABLE_REDIRECTS;
5935 else
5936 request->disable_feature |= WINHTTP_DISABLE_REDIRECTS;
5937 break;
5939 case WinHttpRequestOption_URLCodePage:
5941 VARIANT cp;
5942 VariantInit( &cp );
5943 hr = VariantChangeType( &cp, &value, 0, VT_UI4 );
5944 if (SUCCEEDED( hr ))
5946 request->url_codepage = V_UI4( &cp );
5947 TRACE("URL codepage: %u\n", request->url_codepage);
5949 else if (V_VT( &value ) == VT_BSTR && !wcsicmp( V_BSTR( &value ), L"utf-8" ))
5951 TRACE("URL codepage: UTF-8\n");
5952 request->url_codepage = CP_UTF8;
5953 hr = S_OK;
5955 else
5956 FIXME("URL codepage %s is not recognized\n", debugstr_variant( &value ));
5957 break;
5959 default:
5960 FIXME("unimplemented option %u\n", option);
5961 hr = E_NOTIMPL;
5962 break;
5964 LeaveCriticalSection( &request->cs );
5965 return hr;
5968 static HRESULT WINAPI winhttp_request_WaitForResponse(
5969 IWinHttpRequest *iface,
5970 VARIANT timeout,
5971 VARIANT_BOOL *succeeded )
5973 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
5974 DWORD err, msecs = (V_I4(&timeout) == -1) ? INFINITE : V_I4(&timeout) * 1000;
5976 TRACE("%p, %s, %p\n", request, debugstr_variant(&timeout), succeeded);
5978 EnterCriticalSection( &request->cs );
5979 if (request->state >= REQUEST_STATE_RESPONSE_RECEIVED)
5981 LeaveCriticalSection( &request->cs );
5982 return S_OK;
5984 switch ((err = request_wait( request, msecs )))
5986 case ERROR_TIMEOUT:
5987 if (succeeded) *succeeded = VARIANT_FALSE;
5988 err = ERROR_SUCCESS;
5989 break;
5991 default:
5992 if (succeeded) *succeeded = VARIANT_TRUE;
5993 break;
5995 LeaveCriticalSection( &request->cs );
5996 return HRESULT_FROM_WIN32( err );
5999 static HRESULT WINAPI winhttp_request_Abort(
6000 IWinHttpRequest *iface )
6002 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
6004 TRACE("%p\n", request);
6006 EnterCriticalSection( &request->cs );
6007 cancel_request( request );
6008 LeaveCriticalSection( &request->cs );
6009 return S_OK;
6012 static HRESULT WINAPI winhttp_request_SetTimeouts(
6013 IWinHttpRequest *iface,
6014 LONG resolve_timeout,
6015 LONG connect_timeout,
6016 LONG send_timeout,
6017 LONG receive_timeout )
6019 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
6021 TRACE( "%p, %ld, %ld, %ld, %ld\n", request, resolve_timeout, connect_timeout, send_timeout, receive_timeout );
6023 EnterCriticalSection( &request->cs );
6024 request->resolve_timeout = resolve_timeout;
6025 request->connect_timeout = connect_timeout;
6026 request->send_timeout = send_timeout;
6027 request->receive_timeout = receive_timeout;
6028 LeaveCriticalSection( &request->cs );
6029 return S_OK;
6032 static HRESULT WINAPI winhttp_request_SetClientCertificate(
6033 IWinHttpRequest *iface,
6034 BSTR certificate )
6036 FIXME("\n");
6037 return E_NOTIMPL;
6040 static HRESULT WINAPI winhttp_request_SetAutoLogonPolicy(
6041 IWinHttpRequest *iface,
6042 WinHttpRequestAutoLogonPolicy policy )
6044 struct winhttp_request *request = impl_from_IWinHttpRequest( iface );
6045 HRESULT hr = S_OK;
6047 TRACE("%p, %u\n", request, policy );
6049 EnterCriticalSection( &request->cs );
6050 switch (policy)
6052 case AutoLogonPolicy_Always:
6053 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
6054 break;
6055 case AutoLogonPolicy_OnlyIfBypassProxy:
6056 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM;
6057 break;
6058 case AutoLogonPolicy_Never:
6059 request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH;
6060 break;
6061 default: hr = E_INVALIDARG;
6062 break;
6064 LeaveCriticalSection( &request->cs );
6065 return hr;
6068 static const struct IWinHttpRequestVtbl winhttp_request_vtbl =
6070 winhttp_request_QueryInterface,
6071 winhttp_request_AddRef,
6072 winhttp_request_Release,
6073 winhttp_request_GetTypeInfoCount,
6074 winhttp_request_GetTypeInfo,
6075 winhttp_request_GetIDsOfNames,
6076 winhttp_request_Invoke,
6077 winhttp_request_SetProxy,
6078 winhttp_request_SetCredentials,
6079 winhttp_request_Open,
6080 winhttp_request_SetRequestHeader,
6081 winhttp_request_GetResponseHeader,
6082 winhttp_request_GetAllResponseHeaders,
6083 winhttp_request_Send,
6084 winhttp_request_get_Status,
6085 winhttp_request_get_StatusText,
6086 winhttp_request_get_ResponseText,
6087 winhttp_request_get_ResponseBody,
6088 winhttp_request_get_ResponseStream,
6089 winhttp_request_get_Option,
6090 winhttp_request_put_Option,
6091 winhttp_request_WaitForResponse,
6092 winhttp_request_Abort,
6093 winhttp_request_SetTimeouts,
6094 winhttp_request_SetClientCertificate,
6095 winhttp_request_SetAutoLogonPolicy
6098 HRESULT WinHttpRequest_create( void **obj )
6100 struct winhttp_request *request;
6102 TRACE("%p\n", obj);
6104 if (!(request = calloc( 1, sizeof(*request) ))) return E_OUTOFMEMORY;
6105 request->IWinHttpRequest_iface.lpVtbl = &winhttp_request_vtbl;
6106 request->refs = 1;
6107 InitializeCriticalSectionEx( &request->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO );
6108 request->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": winhttp_request.cs");
6109 initialize_request( request );
6111 *obj = &request->IWinHttpRequest_iface;
6112 TRACE("returning iface %p\n", *obj);
6113 return S_OK;