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
32 #include "httprequest.h"
33 #include "httprequestid.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
))
157 static struct task_header
*get_next_task( struct queue
*queue
, struct task_header
*prev_task
)
159 struct task_header
*task
;
162 AcquireSRWLockExclusive( &queue
->lock
);
163 assert( queue
->callback_running
);
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
);
177 queue
->callback_running
= FALSE
;
179 ReleaseSRWLockExclusive( &queue
->lock
);
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
);
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
);
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;
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
;
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
;
248 AcquireSRWLockExclusive( &queue
->lock
);
250 LIST_FOR_EACH_ENTRY( task_hdr
, &queue
->queued_tasks
, struct task_header
, entry
)
252 if (task_needs_completion( task_hdr
))
255 addref_task( found
);
259 ReleaseSRWLockExclusive( &queue
->lock
);
262 found
->callback( found
, TRUE
);
263 release_task( found
);
268 static void task_send_callback( void *ctx
, BOOL abort
)
270 struct send_callback
*s
= ctx
;
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
);
285 static BOOL
valid_token_char( WCHAR c
)
287 if (c
< 32 || c
== 127) return FALSE
;
294 case '\\': case '\"':
306 static struct header
*parse_header( const WCHAR
*string
, size_t string_len
)
309 struct header
*header
;
313 if (!(q
= wcschr( p
, ':' )))
315 WARN("no ':' in line %s\n", debugstr_w(string
));
320 WARN("empty field name in line %s\n", debugstr_w(string
));
325 if (!valid_token_char( *p
))
327 WARN("invalid character in field name %s\n", debugstr_w(string
));
333 if (!(header
= calloc( 1, sizeof(*header
) ))) return NULL
;
334 if (!(header
->field
= malloc( (len
+ 1) * sizeof(WCHAR
) )))
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
);
351 memcpy( header
->value
, q
, len
* sizeof(WCHAR
) );
352 header
->value
[len
] = 0;
357 static int get_header_index( struct request
*request
, const WCHAR
*field
, int requested_index
, BOOL request_only
)
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;
372 if (index
>= request
->num_headers
) index
= -1;
373 TRACE("returning %d\n", index
);
377 static DWORD
insert_header( struct request
*request
, struct header
*header
)
379 DWORD count
= request
->num_headers
+ 1;
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
)
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
)
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
);
440 if ((flags
& (WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA
| WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON
)) &&
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
;
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
;
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
;
478 if (len
== ~0u) len
= lstrlenW( headers
);
479 if (!len
) return ERROR_SUCCESS
;
486 if (p
>= headers
+ len
) break;
488 for (q
= p
; q
< headers
+ len
&& *q
!= '\r' && *q
!= '\n'; ++q
)
491 while (*q
== '\r' || *q
== '\n')
494 if ((header
= parse_header( p
, end
- p
)))
496 ret
= process_header( request
, header
->field
, header
->value
, flags
, TRUE
);
497 free_header( header
);
505 /***********************************************************************
506 * WinHttpAddRequestHeaders (winhttp.@)
508 BOOL WINAPI
WinHttpAddRequestHeaders( HINTERNET hrequest
, const WCHAR
*headers
, DWORD len
, DWORD flags
)
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
);
520 if (!(request
= (struct request
*)grab_object( hrequest
)))
522 SetLastError( ERROR_INVALID_HANDLE
);
525 if (request
->hdr
.type
!= WINHTTP_HANDLE_TYPE_REQUEST
)
527 release_object( &request
->hdr
);
528 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE
);
532 ret
= add_request_headers( request
, headers
, len
, flags
);
534 release_object( &request
->hdr
);
539 static WCHAR
*build_absolute_request_path( struct request
*request
, const WCHAR
**path
)
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
;
565 static WCHAR
*build_request_string( struct request
*request
)
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
);
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
,
614 struct header
*header
= NULL
;
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
;
625 case WINHTTP_QUERY_CUSTOM
:
627 header_index
= get_header_index( request
, name
, requested_index
, request_only
);
630 case WINHTTP_QUERY_RAW_HEADERS
:
632 WCHAR
*headers
, *p
, *q
;
635 headers
= build_request_string( request
);
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
;
645 for (p
= headers
, q
= buffer
; *p
; p
++, q
++)
647 if (*p
!= '\r') *q
= *p
;
654 TRACE("returning data: %s\n", debugstr_wn(buffer
, len
));
658 *buflen
= len
* sizeof(WCHAR
);
659 if (request_only
) free( headers
);
662 case WINHTTP_QUERY_RAW_HEADERS_CRLF
:
667 headers
= build_request_string( request
);
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
;
680 memcpy( buffer
, headers
, len
+ sizeof(WCHAR
) );
681 TRACE("returning data: %s\n", debugstr_wn(buffer
, len
/ sizeof(WCHAR
)));
685 if (request_only
) free( headers
);
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
;
697 lstrcpyW( buffer
, request
->version
);
698 TRACE("returning string: %s\n", debugstr_w(buffer
));
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
;
713 lstrcpyW( buffer
, request
->status_text
);
714 TRACE("returning string: %s\n", debugstr_w(buffer
));
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
;
729 lstrcpyW( buffer
, request
->verb
);
730 TRACE("returning string: %s\n", debugstr_w(buffer
));
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
);
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
;
758 DWORD
*number
= buffer
;
759 *number
= wcstoul( header
->value
, NULL
, 10 );
760 TRACE("returning number: %lu\n", *number
);
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
);
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
;
788 lstrcpyW( buffer
, header
->value
);
789 TRACE("returning string: %s\n", debugstr_w(buffer
));
794 if (!ret
&& index
) *index
+= 1;
798 /***********************************************************************
799 * WinHttpQueryHeaders (winhttp.@)
801 BOOL WINAPI
WinHttpQueryHeaders( HINTERNET hrequest
, DWORD level
, const WCHAR
*name
, void *buffer
, DWORD
*buflen
,
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
);
814 if (request
->hdr
.type
!= WINHTTP_HANDLE_TYPE_REQUEST
)
816 release_object( &request
->hdr
);
817 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE
);
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
);
828 ret
= query_headers( request
, level
, name
, buffer
, buflen
, index
);
830 release_object( &request
->hdr
);
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
)
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
)
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
;
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;
880 ret
= query_headers( request
, level
, NULL
, NULL
, &size
, &index
);
881 if (ret
!= ERROR_INSUFFICIENT_BUFFER
)
883 if (index
) ret
= ERROR_SUCCESS
;
887 if (!(buffer
= malloc( size
))) return ERROR_OUTOFMEMORY
;
888 if ((ret
= query_headers( request
, level
, NULL
, buffer
, &size
, &index
)))
893 scheme
= auth_scheme_from_header( buffer
);
895 if (!scheme
) continue;
897 if (!first_scheme
) first_scheme
= scheme
;
898 supported_schemes
|= scheme
;
903 *supported
= supported_schemes
;
904 *first
= first_scheme
;
909 /***********************************************************************
910 * WinHttpQueryAuthSchemes (winhttp.@)
912 BOOL WINAPI
WinHttpQueryAuthSchemes( HINTERNET hrequest
, LPDWORD supported
, LPDWORD first
, LPDWORD target
)
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
);
924 if (request
->hdr
.type
!= WINHTTP_HANDLE_TYPE_REQUEST
)
926 release_object( &request
->hdr
);
927 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE
);
930 if (!supported
|| !first
|| !target
)
932 release_object( &request
->hdr
);
933 SetLastError( ERROR_INVALID_PARAMETER
);
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
);
953 static UINT
encode_base64( const char *bin
, unsigned int len
, WCHAR
*base64
)
956 static const char base64enc
[] =
957 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
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] */
968 base64
[n
++] = base64enc
[x
];
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] */
979 base64
[n
++] = base64enc
[x
];
983 base64
[n
++] = base64enc
[x
| ((bin
[2] & 0xc0) >> 6)];
985 /* last 6 bits, all from bin [2] */
986 base64
[n
++] = base64enc
[bin
[2] & 0x3f];
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;
1004 static unsigned int decode_base64( const WCHAR
*base64
, unsigned int len
, char *buf
)
1007 char c0
, c1
, c2
, c3
;
1008 const WCHAR
*p
= base64
;
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;
1019 buf
[i
+ 0] = (c0
<< 2) | (c1
>> 4);
1020 buf
[i
+ 1] = (c1
<< 4) | (c2
>> 2);
1021 buf
[i
+ 2] = (c2
<< 6) | c3
;
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);
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;
1043 buf
[i
+ 0] = (c0
<< 2) | (c1
>> 4);
1044 buf
[i
+ 1] = (c1
<< 4) | (c2
>> 2);
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;
1057 buf
[i
+ 0] = (c0
<< 2) | (c1
>> 4);
1058 buf
[i
+ 1] = (c1
<< 4) | (c2
>> 2);
1059 buf
[i
+ 2] = (c2
<< 6) | c3
;
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
) );
1080 ret
->finished
= FALSE
;
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
);
1097 static BOOL
get_authvalue( struct request
*request
, DWORD level
, DWORD scheme
, WCHAR
*buffer
, DWORD len
)
1099 DWORD size
, index
= 0;
1103 if (query_headers( request
, level
, NULL
, buffer
, &size
, &index
)) return FALSE
;
1104 if (auth_scheme_from_header( buffer
) == scheme
) break;
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
;
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
;
1134 if (!has_auth_value
) return FALSE
;
1135 username
= request
->connect
->username
;
1136 password
= request
->connect
->password
;
1140 case WINHTTP_AUTH_TARGET_PROXY
:
1141 if (!get_authvalue( request
, WINHTTP_QUERY_PROXY_AUTHENTICATE
, scheme_flag
, auth_value
, len
))
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
;
1152 username
= request
->connect
->session
->proxy_username
;
1153 password
= request
->connect
->session
->proxy_password
;
1158 WARN( "unknown target %#lx\n", target
);
1161 authinfo
= *auth_ptr
;
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
;
1187 case SCHEME_NEGOTIATE
:
1189 SECURITY_STATUS status
;
1190 SecBufferDesc out_desc
, in_desc
;
1192 ULONG flags
= ISC_REQ_CONNECTION
|ISC_REQ_USE_DCE_STYLE
|ISC_REQ_MUTUAL_AUTH
|ISC_REQ_DELEGATE
;
1199 SEC_WINNT_AUTH_IDENTITY_W id
;
1200 WCHAR
*domain
, *user
;
1202 if (!username
|| !password
|| !(authinfo
= alloc_authinfo())) return FALSE
;
1205 domain
= (WCHAR
*)username
;
1206 user
= wcschr( username
, '\\' );
1211 user
= (WCHAR
*)username
;
1214 id
.Flags
= SEC_WINNT_AUTH_IDENTITY_UNICODE
;
1216 id
.UserLength
= lstrlenW( user
);
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
)
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
);
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
);
1255 in
.BufferType
= SECBUFFER_TOKEN
;
1259 in_desc
.ulVersion
= 0;
1260 in_desc
.cBuffers
= 1;
1261 in_desc
.pBuffers
= &in
;
1263 p
= auth_value
+ auth_schemes
[scheme
].len
;
1266 int len
= lstrlenW( ++p
);
1267 in
.cbBuffer
= decode_base64( p
, len
, NULL
);
1268 if (!(in
.pvBuffer
= malloc( in
.cbBuffer
))) {
1269 destroy_authinfo( authinfo
);
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
);
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");
1310 ERR( "InitializeSecurityContextW failed with error %#lx\n", status
);
1311 free( out
.pvBuffer
);
1312 destroy_authinfo( authinfo
);
1319 ERR("invalid scheme %u\n", scheme
);
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
);
1338 static WCHAR
*build_proxy_connect_string( struct request
*request
)
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" );
1382 static DWORD
read_reply( struct request
*request
);
1384 static DWORD
secure_proxy_connect( struct request
*request
)
1388 int len
, bytes_sent
;
1391 if (!(str
= build_proxy_connect_string( request
))) return ERROR_OUTOFMEMORY
;
1392 strA
= strdupWA( str
);
1394 if (!strA
) return ERROR_OUTOFMEMORY
;
1396 len
= strlen( strA
);
1397 ret
= netconn_send( request
->netconn
, strA
, len
, &bytes_sent
, NULL
);
1399 if (!ret
) ret
= read_reply( request
);
1404 static WCHAR
*addr_to_str( struct sockaddr_storage
*addr
)
1406 char buf
[INET6_ADDRSTRLEN
];
1409 switch (addr
->ss_family
)
1412 src
= &((struct sockaddr_in
*)addr
)->sin_addr
;
1415 src
= &((struct sockaddr_in6
*)addr
)->sin6_addr
;
1418 WARN("unsupported address family %d\n", addr
->ss_family
);
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
)
1440 EnterCriticalSection( &connection_pool_cs
);
1441 if (!(ref
= --host
->ref
)) list_remove( &host
->entry
);
1442 LeaveCriticalSection( &connection_pool_cs
);
1445 assert( list_empty( &host
->connections
) );
1446 free( host
->hostname
);
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
;
1461 /* FIXME: Use more sophisticated method */
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
)
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
)
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
;
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
)
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
;
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
);
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
;
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
)
1583 if ((host
= malloc( sizeof(*host
) )))
1586 host
->secure
= is_secure
;
1588 list_init( &host
->connections
);
1589 if ((host
->hostname
= wcsdup( connect
->servername
)))
1591 list_add_head( &connection_pool
, &host
->entry
);
1601 LeaveCriticalSection( &connection_pool_cs
);
1603 if (!host
) return ERROR_OUTOFMEMORY
;
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
);
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
);
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
);
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
)))
1665 release_host( host
);
1668 netconn_set_timeout( netconn
, TRUE
, request
->send_timeout
);
1669 netconn_set_timeout( netconn
, FALSE
, request_receive_response_timeout( request
));
1671 request
->netconn
= netconn
;
1675 if (connect
->session
->proxy_server
&& wcsicmp( connect
->hostname
, connect
->servername
))
1677 if ((ret
= secure_proxy_connect( request
)))
1679 request
->netconn
= NULL
;
1681 netconn_release( netconn
);
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
;
1695 netconn_release( netconn
);
1700 send_callback( &request
->hdr
, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER
, addressW
, lstrlenW(addressW
) + 1 );
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
)))
1714 netconn_release( netconn
);
1715 return ERROR_WINHTTP_SECURE_FAILURE
;
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
;
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
)
1739 struct connect
*connect
= request
->connect
;
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
);
1756 static void clear_response_headers( struct request
*request
)
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
);
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
)
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
;
1805 /* discard data contents until we reach end of line */
1806 static DWORD
discard_eol( struct request
*request
, BOOL notify
)
1811 char *eol
= memchr( request
->read_buf
+ request
->read_pos
, '\n', request
->read_size
);
1814 remove_data( request
, (eol
+ 1) - (request
->read_buf
+ request
->read_pos
) );
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
)
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
;
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
) );
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
;
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
);
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 );
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
;
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
;
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
);
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
);
2009 if (!ret
) send_callback( &request
->hdr
, WINHTTP_CALLBACK_STATUS_READ_COMPLETE
, buffer
, bytes_read
);
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
;
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
;
2029 refill_buffer( request
, FALSE
);
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
;
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
; }
2070 static DWORD
escape_string( const char *src
, DWORD len
, char *dst
, enum escape_flags flags
)
2072 static const char hex
[] = "0123456789ABCDEF";
2076 for (i
= 0; i
< len
; i
++)
2078 if ((flags
& ESCAPE_FLAG_STRIP_CRLF
) && (src
[i
] == '\r' || src
[i
] == '\n'))
2083 if (need_escape( src
[i
], flags
))
2088 ptr
[1] = hex
[(src
[i
] >> 4) & 0xf];
2089 ptr
[2] = hex
[src
[i
] & 0xf];
2094 else if (dst
) *ptr
++ = src
[i
];
2097 if (dst
) dst
[ret
] = 0;
2101 static DWORD
str_to_wire( const WCHAR
*src
, int src_len
, char *dst
, enum escape_flags flags
)
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
);
2117 static char *build_wire_path( struct request
*request
, DWORD
*ret_len
)
2120 const WCHAR
*start
, *path
, *query
= NULL
;
2121 DWORD len
, len_path
= 0, len_query
= 0;
2122 enum escape_flags path_flags
, query_flags
;
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
);
2161 static char *build_wire_request( struct request
*request
, DWORD
*len
)
2163 char *path
, *ptr
, *ret
;
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 );
2186 memcpy( ptr
, path
, len_path
);
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
)
2197 ptr
+= str_to_wire( request
->headers
[i
].field
, -1, ptr
, 0 );
2200 ptr
+= str_to_wire( request
->headers
[i
].value
, -1, ptr
, 0 );
2203 memcpy( ptr
, "\r\n\r\n", sizeof("\r\n\r\n") );
2210 static WCHAR
*create_websocket_key(void)
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
);
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
);
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
;
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
;
2254 drain_content( request
);
2255 clear_response_headers( request
);
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
);
2299 if (!(request
->hdr
.disable_flags
& WINHTTP_DISABLE_COOKIES
) && (ret
= add_cookie_headers( request
)))
2301 WARN( "failed to add cookie headers: %lu\n", 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
;
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
);
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
;
2343 request
->state
= REQUEST_RESPONSE_STATE_REPLY_RECEIVED
;
2347 if (request
->state
== REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED
)
2348 request
->state
= REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED_REQUEST_SENT
;
2350 request
->state
= REQUEST_RESPONSE_STATE_REQUEST_SENT
;
2356 if (!ret
) send_callback( &request
->hdr
, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE
, NULL
, 0 );
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
) );
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
;
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
);
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
)
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
);
2398 if (request
->hdr
.type
!= WINHTTP_HANDLE_TYPE_REQUEST
)
2400 release_object( &request
->hdr
);
2401 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE
);
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
);
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
)))
2430 else ret
= send_request( request
, headers
, headers_len
, optional
, optional_len
, total_len
, context
, FALSE
);
2432 release_object( &request
->hdr
);
2433 SetLastError( 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
;
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
;
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
;
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
)
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
);
2493 if (request
->hdr
.type
!= WINHTTP_HANDLE_TYPE_REQUEST
)
2495 release_object( &request
->hdr
);
2496 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE
);
2500 ret
= set_credentials( request
, target
, scheme
, username
, password
);
2502 release_object( &request
->hdr
);
2503 SetLastError( ret
);
2507 static DWORD
handle_authorization( struct request
*request
, DWORD status
)
2509 DWORD ret
, i
, schemes
, first
, level
, target
;
2513 case HTTP_STATUS_DENIED
:
2514 target
= WINHTTP_AUTH_TARGET_SERVER
;
2515 level
= WINHTTP_QUERY_WWW_AUTHENTICATE
;
2518 case HTTP_STATUS_PROXY_AUTH_REQ
:
2519 target
= WINHTTP_AUTH_TARGET_PROXY
;
2520 level
= WINHTTP_QUERY_PROXY_AUTHENTICATE
;
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
;
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
)
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;
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;
2577 char *eol
= memchr( request
->read_buf
+ request
->read_pos
, '\n', request
->read_size
);
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
);
2588 remove_data( request
, bytes_read
);
2591 if ((ret
= read_more_data( request
, -1, FALSE
))) return ret
;
2592 if (!request
->read_size
)
2595 TRACE("returning empty string\n");
2596 return ERROR_WINHTTP_INVALID_SERVER_RESPONSE
;
2601 if (pos
&& buffer
[pos
- 1] == '\r') pos
--;
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
;
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
;
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;
2673 struct header
*header
;
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
)
2684 if (!(tmp
= realloc( raw_headers
, len
* sizeof(WCHAR
) ))) return ERROR_OUTOFMEMORY
;
2685 request
->raw_headers
= raw_headers
= tmp
;
2689 memcpy( raw_headers
+ offset
, L
"\r\n", sizeof(L
"\r\n") );
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
);
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
));
2709 static void record_cookies( struct request
*request
)
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
)
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
)))
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;
2745 struct connect
*connect
= request
->connect
;
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 */
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
) );
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
) );
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 );
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
;
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
;
2808 memcpy( hostname
, uc
.lpszHostName
, len
* sizeof(WCHAR
) );
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
;
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;
2858 static BOOL
is_passport_request( struct request
*request
)
2860 static const WCHAR passportW
[] = {'P','a','s','s','p','o','r','t','1','.','4'};
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
;
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')
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
;
2900 if (!(r
= malloc( sizeof(*r
) ))) return ERROR_OUTOFMEMORY
;
2901 if ((ret
= queue_task( &request
->queue
, task_receive_response
, &r
->task_hdr
, &request
->hdr
)))
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 );
2921 case REQUEST_RESPONSE_STATE_SENDING_REQUEST
:
2924 ret
= ERROR_WINHTTP_INCORRECT_HANDLE_STATE
;
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 );
2936 request
->state
= REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED_REQUEST_SENT
;
2937 return queue_receive_response( request
);
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
;
2946 case REQUEST_RESPONSE_STATE_REPLY_RECEIVED
:
2947 send_callback( &request
->hdr
, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE
, NULL
, 0 );
2950 case REQUEST_RESPONSE_STATE_READ_RESPONSE_QUEUED_REPLY_RECEIVED
:
2951 request
->state
= REQUEST_RESPONSE_STATE_REPLY_RECEIVED
;
2955 ret
= ERROR_WINHTTP_INCORRECT_HANDLE_STATE
;
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
);
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
;
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
;
2997 request
->state
= REQUEST_RESPONSE_RECURSIVE_REQUEST
;
2998 return async_mode
? queue_receive_response( request
) : receive_response( request
);
3003 request
->state
= REQUEST_RESPONSE_STATE_RESPONSE_RECEIVED
;
3004 if (request
->netconn
) netconn_set_timeout( request
->netconn
, FALSE
, request
->receive_timeout
);
3008 if (!ret
) send_callback( &request
->hdr
, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE
, NULL
, 0 );
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
;
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
;
3028 TRACE("running %p\n", ctx
);
3029 receive_response( request
);
3032 /***********************************************************************
3033 * WinHttpReceiveResponse (winhttp.@)
3035 BOOL WINAPI
WinHttpReceiveResponse( HINTERNET hrequest
, LPVOID reserved
)
3038 struct request
*request
;
3040 TRACE("%p, %p\n", hrequest
, reserved
);
3042 if (!(request
= (struct request
*)grab_object( hrequest
)))
3044 SetLastError( ERROR_INVALID_HANDLE
);
3047 if (request
->hdr
.type
!= WINHTTP_HANDLE_TYPE_REQUEST
)
3049 release_object( &request
->hdr
);
3050 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE
);
3054 ret
= receive_response( request
);
3056 release_object( &request
->hdr
);
3057 SetLastError( ret
);
3061 static DWORD
query_data_ready( struct request
*request
)
3065 count
= get_available_data( request
);
3066 if (!request
->read_chunked
&& request
->netconn
) count
+= netconn_query_data_available( request
->netconn
);
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
);
3092 TRACE( "%lu bytes available\n", count
);
3095 if (!ret
) send_callback( &request
->hdr
, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE
, &count
, sizeof(count
) );
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
;
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
;
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
)
3126 struct request
*request
;
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
);
3137 if (request
->hdr
.type
!= WINHTTP_HANDLE_TYPE_REQUEST
)
3139 release_object( &request
->hdr
);
3140 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE
);
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
);
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
);
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
)))
3179 ret
= ERROR_IO_PENDING
;
3183 struct query_data
*q
;
3185 if (!(q
= malloc( sizeof(*q
) )))
3187 release_object( &request
->hdr
);
3188 SetLastError( ERROR_OUTOFMEMORY
);
3192 q
->available
= available
;
3194 if ((ret
= queue_task( &request
->queue
, task_query_data_available
, &q
->task_hdr
, &request
->hdr
)))
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
;
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
)
3222 struct request
*request
;
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
);
3233 if (request
->hdr
.type
!= WINHTTP_HANDLE_TYPE_REQUEST
)
3235 release_object( &request
->hdr
);
3236 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE
);
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
);
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
;
3261 s
->buflen
= s
->count
;
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
)))
3275 ret
= ERROR_IO_PENDING
;
3279 struct read_data
*r
;
3281 if (!(r
= malloc( sizeof(*r
) )))
3283 release_object( &request
->hdr
);
3284 SetLastError( ERROR_OUTOFMEMORY
);
3288 r
->to_read
= to_read
;
3291 if ((ret
= queue_task( &request
->queue
, task_read_data
, &r
->task_hdr
, &request
->hdr
)))
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
)
3307 ret
= netconn_send( request
->netconn
, buffer
, to_write
, &num_bytes
, NULL
);
3311 if (!ret
) send_callback( &request
->hdr
, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE
, &num_bytes
, sizeof(num_bytes
) );
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
;
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
;
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
)
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
);
3350 if (request
->hdr
.type
!= WINHTTP_HANDLE_TYPE_REQUEST
)
3352 release_object( &request
->hdr
);
3353 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE
);
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
);
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
)))
3374 else ret
= write_data( request
, buffer
, to_write
, written
, FALSE
);
3376 release_object( &request
->hdr
);
3377 SetLastError( ret
);
3381 static void socket_handle_closing( struct object_header
*hdr
)
3383 struct socket
*socket
= (struct socket
*)hdr
;
3386 pending_tasks
= cancel_queue( &socket
->send_q
);
3387 pending_tasks
= cancel_queue( &socket
->recv_q
) || pending_tasks
;
3390 netconn_cancel_io( socket
->netconn
);
3393 static BOOL
socket_query_option( struct object_header
*hdr
, DWORD option
, void *buffer
, DWORD
*buflen
)
3397 case WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL
:
3398 SetLastError( ERROR_INVALID_PARAMETER
);
3402 FIXME( "unimplemented option %lu\n", option
);
3403 SetLastError( ERROR_WINHTTP_INVALID_OPTION
);
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
);
3422 static BOOL
socket_set_option( struct object_header
*hdr
, DWORD option
, void *buffer
, DWORD buflen
)
3424 struct socket
*socket
= (struct socket
*)hdr
;
3428 case WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_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
);
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
);
3446 FIXME( "unimplemented option %lu\n", option
);
3447 SetLastError( ERROR_WINHTTP_INVALID_OPTION
);
3451 static const struct object_vtbl socket_vtbl
=
3453 socket_handle_closing
,
3455 socket_query_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
);
3472 if (request
->hdr
.type
!= WINHTTP_HANDLE_TYPE_REQUEST
)
3474 release_object( &request
->hdr
);
3475 SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE
);
3478 if (!(socket
= calloc( 1, sizeof(*socket
) )))
3480 release_object( &request
->hdr
);
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" );
3498 release_object( &request
->hdr
);
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
);
3525 static DWORD
send_bytes( struct socket
*socket
, char *bytes
, int len
, int *sent
, WSAOVERLAPPED
*ovr
)
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;
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;
3554 hdr
[1] = (char)MASK_BIT
;
3555 if (len
< 126) hdr
[1] |= len
;
3556 else if (len
< 65536)
3560 hdr
[3] = len
& 0xff;
3566 hdr
[2] = hdr
[3] = hdr
[4] = hdr
[5] = 0;
3568 hdr
[7] = (len
>> 16) & 0xff;
3569 hdr
[8] = (len
>> 8) & 0xff;
3570 hdr
[9] = len
& 0xff;
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
)
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
);
3595 RtlGenRandom( socket
->mask
, 4 );
3596 memcpy( ptr
, socket
->mask
, 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];
3619 ret
= send_bytes( socket
, socket
->send_frame_buffer
, offset
, &sent_size
, ovr
);
3620 socket
->send_remaining_size
-= sent_size
;
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
;
3630 assert( sent_size
== offset
);
3634 return ERROR_SUCCESS
;
3637 static DWORD
complete_send_frame( struct socket
*socket
, WSAOVERLAPPED
*ovr
, const char *buf
)
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
;
3711 static enum socket_opcode
map_buffer_type( struct socket
*socket
, WINHTTP_WEB_SOCKET_BUFFER_TYPE 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
;
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
)
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
) );
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
;
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
;
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;
3854 memset( &s
->ovr
, 0, sizeof(s
->ovr
) );
3855 if ((ret
= socket_send( socket
, type
, buf
, len
, &s
->ovr
)) == WSA_IO_PENDING
)
3858 complete_async
= TRUE
;
3864 s
->complete_async
= complete_async
;
3865 TRACE("queueing, complete_async %#x.\n", complete_async
);
3870 if ((ret
= queue_task( &socket
->send_q
, task_socket_send
, &s
->task_hdr
, &socket
->hdr
)))
3873 if (!async_send
|| ret
)
3875 InterlockedDecrement( &socket
->hdr
.pending_sends
);
3876 InterlockedExchange( &socket
->pending_noncontrol_send
, 0 );
3878 ReleaseSRWLockExclusive( &socket
->send_lock
);
3881 TRACE("sent sync.\n");
3883 socket_send_complete( socket
, ret
, type
, len
);
3884 ret
= ERROR_SUCCESS
;
3889 if (validate_buffer_type( type
, socket
->sending_fragment_type
))
3891 ret
= socket_send( socket
, type
, buf
, len
, NULL
);
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
);
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
;
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
;
3921 if ((err
= netconn_recv( socket
->netconn
, ptr
, needed
, 0, &received
))) return err
;
3922 if (!received
) break;
3924 if (!read_full_buffer
) break;
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
)
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
:
3945 FIXME( "opcode %02x not handled\n", opcode
);
3950 static DWORD
receive_frame( struct socket
*socket
, DWORD
*ret_len
, enum socket_opcode
*opcode
, BOOL
*final
)
3952 DWORD ret
, len
, count
;
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
;
3968 if ((ret
= receive_bytes( socket
, (char *)&len16
, sizeof(len16
), &count
, TRUE
))) return ret
;
3969 len
= RtlUshortByteSwap( len16
);
3971 else if (len
== 127)
3974 if ((ret
= receive_bytes( socket
, (char *)&len64
, sizeof(len64
), &count
, TRUE
))) return ret
;
3975 if ((len64
= RtlUlonglongByteSwap( len64
)) > ~0u) return ERROR_NOT_SUPPORTED
;
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
;
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
;
4006 if (!(s
= malloc( sizeof(*s
) ))) return ERROR_OUTOFMEMORY
;
4008 AcquireSRWLockExclusive( &socket
->send_lock
);
4009 async_send
= InterlockedIncrement( &socket
->hdr
.pending_sends
) > 1;
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
)
4016 complete_async
= TRUE
;
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
);
4031 InterlockedDecrement( &socket
->hdr
.pending_sends
);
4034 ReleaseSRWLockExclusive( &socket
->send_lock
);
4037 return send_frame( socket
, SOCKET_OPCODE_PONG
, 0, NULL
, 0, TRUE
, NULL
);
4040 static DWORD
socket_drain( struct socket
*socket
)
4044 while (socket
->read_size
)
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
)
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;
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
;
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
);
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
);
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
:
4137 FIXME( "Received SOCKET_OPCODE_CONTINUE without starting fragment.\n" );
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
;
4153 FIXME("opcode %02x not handled\n", opcode
);
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
)
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
);
4179 socket
->last_receive_final
= final
;
4180 ret
= receive_bytes( socket
, buf
, min(len
, socket
->read_size
), &count
, FALSE
);
4184 if (count
< socket
->read_size
)
4185 WARN("Short read.\n");
4187 socket
->read_size
-= 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
;
4201 static void socket_receive_complete( struct socket
*socket
, DWORD ret
, WINHTTP_WEB_SOCKET_BUFFER_TYPE type
, DWORD len
)
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
) );
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
;
4225 WINHTTP_WEB_SOCKET_BUFFER_TYPE type
;
4229 socket_receive_complete( socket
, ERROR_WINHTTP_OPERATION_CANCELLED
, 0, 0 );
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
;
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
;
4283 if ((ret
= queue_task( &socket
->recv_q
, task_socket_receive
, &r
->task_hdr
, &socket
->hdr
)))
4285 InterlockedDecrement( &socket
->hdr
.pending_receives
);
4289 else ret
= socket_receive( socket
, buf
, len
, ret_len
, ret_type
);
4291 release_object( &socket
->hdr
);
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 );
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
;
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
,
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;
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
)
4347 complete_async
= TRUE
;
4353 s
->complete_async
= complete_async
;
4355 memcpy( s
->reason
, reason
, 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
);
4365 else InterlockedDecrement( &socket
->hdr
.pending_sends
);
4366 ReleaseSRWLockExclusive( &socket
->send_lock
);
4372 socket_shutdown_complete( socket
, ret
);
4373 ret
= ERROR_SUCCESS
;
4377 else ret
= send_frame( socket
, SOCKET_OPCODE_CLOSE
, status
, reason
, len
, TRUE
, NULL
);
4382 DWORD WINAPI
WinHttpWebSocketShutdown( HINTERNET hsocket
, USHORT status
, void *reason
, DWORD len
)
4384 struct socket
*socket
;
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
);
4408 static DWORD
socket_close( struct socket
*socket
)
4413 if (socket
->close_frame_received
) return socket
->close_frame_receive_err
;
4415 if ((ret
= socket_drain( socket
))) return ret
;
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
;
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 );
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
;
4452 socket_close_complete( socket
, ERROR_WINHTTP_OPERATION_CANCELLED
);
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
;
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
)))
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
);
4506 if (socket
->hdr
.flags
& WINHTTP_FLAG_ASYNC
)
4508 struct socket_shutdown
*s
;
4510 if (!(s
= calloc( 1, sizeof(*s
) )))
4512 ret
= ERROR_OUTOFMEMORY
;
4515 if ((ret
= queue_task( &socket
->recv_q
, task_socket_close
, &s
->task_hdr
, &socket
->hdr
)))
4517 InterlockedDecrement( &socket
->hdr
.pending_receives
);
4521 else ret
= socket_close( socket
);
4524 release_object( &socket
->hdr
);
4528 DWORD WINAPI
WinHttpWebSocketQueryCloseStatus( HINTERNET hsocket
, USHORT
*status
, void *reason
, DWORD len
,
4531 struct socket
*socket
;
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
);
4551 *status
= socket
->status
;
4552 *ret_len
= socket
->reason_len
;
4553 if (socket
->reason_len
> len
) ret
= ERROR_INSUFFICIENT_BUFFER
;
4556 memcpy( reason
, socket
->reason
, socket
->reason_len
);
4557 ret
= ERROR_SUCCESS
;
4560 release_object( &socket
->hdr
);
4566 REQUEST_STATE_INITIALIZED
,
4567 REQUEST_STATE_CANCELLED
,
4570 REQUEST_STATE_RESPONSE_RECEIVED
4573 struct winhttp_request
4575 IWinHttpRequest IWinHttpRequest_iface
;
4577 CRITICAL_SECTION cs
;
4578 enum request_state state
;
4590 DWORD bytes_available
;
4594 DWORD disable_feature
;
4595 LONG resolve_timeout
;
4596 LONG connect_timeout
;
4598 LONG receive_timeout
;
4599 WINHTTP_PROXY_INFO proxy
;
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
);
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
);
4670 static HRESULT WINAPI
winhttp_request_QueryInterface(
4671 IWinHttpRequest
*iface
,
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
))
4687 FIXME("interface %s not implemented\n", debugstr_guid(riid
));
4688 return E_NOINTERFACE
;
4690 IWinHttpRequest_AddRef( iface
);
4694 static HRESULT WINAPI
winhttp_request_GetTypeInfoCount(
4695 IWinHttpRequest
*iface
,
4698 struct winhttp_request
*request
= impl_from_IWinHttpRequest( iface
);
4700 TRACE("%p, %p\n", request
, count
);
4707 IWinHttpRequest_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
)
4723 if (!winhttp_typelib
)
4727 hr
= LoadRegTypeLib( &LIBID_WinHttp
, 5, 1, LOCALE_SYSTEM_DEFAULT
, &typelib
);
4730 ERR( "LoadRegTypeLib failed: %#lx\n", 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
);
4743 ERR( "GetTypeInfoOfGuid(%s) failed: %#lx\n", debugstr_guid(winhttp_tid_id
[tid
]), 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
]);
4754 void release_typelib(void)
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
,
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
,
4786 struct winhttp_request
*request
= impl_from_IWinHttpRequest( iface
);
4787 ITypeInfo
*typeinfo
;
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
);
4797 hr
= ITypeInfo_GetIDsOfNames( typeinfo
, names
, count
, dispid
);
4798 ITypeInfo_Release( typeinfo
);
4803 static HRESULT WINAPI
winhttp_request_Invoke(
4804 IWinHttpRequest
*iface
,
4811 EXCEPINFO
*excep_info
,
4814 struct winhttp_request
*request
= impl_from_IWinHttpRequest( iface
);
4815 ITypeInfo
*typeinfo
;
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
;
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] );
4843 WARN( "put_Option(%ld) failed: %#lx\n", V_I4( &option
), 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
);
4853 WARN( "get_Option(%ld) failed: %#lx\n", V_I4( &option
), hr
);
4857 FIXME("unsupported flags %x\n", flags
);
4861 /* fallback to standard implementation */
4863 hr
= get_typeinfo( IWinHttpRequest_tid
, &typeinfo
);
4866 hr
= ITypeInfo_Invoke( typeinfo
, &request
->IWinHttpRequest_iface
, member
, flags
,
4867 params
, result
, excep_info
, arg_err
);
4868 ITypeInfo_Release( typeinfo
);
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
;
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
;
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
) );
4919 err
= ERROR_INVALID_PARAMETER
;
4922 LeaveCriticalSection( &request
->cs
);
4923 return HRESULT_FROM_WIN32( err
);
4926 static HRESULT WINAPI
winhttp_request_SetCredentials(
4927 IWinHttpRequest
*iface
,
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
;
4946 case HTTPREQUEST_SETCREDENTIALS_FOR_SERVER
:
4947 target
= WINHTTP_AUTH_TARGET_SERVER
;
4949 case HTTPREQUEST_SETCREDENTIALS_FOR_PROXY
:
4950 target
= WINHTTP_AUTH_TARGET_PROXY
;
4953 err
= ERROR_INVALID_PARAMETER
;
4956 if (!WinHttpSetCredentials( request
->hrequest
, target
, scheme
, username
, password
, NULL
))
4958 err
= GetLastError();
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
,
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
);
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();
5058 if (!(request
->hconnect
= WinHttpConnect( request
->hsession
, hostname
, uc
.nPort
, 0 )))
5060 WinHttpCloseHandle( request
->hsession
);
5061 request
->hsession
= NULL
;
5062 err
= GetLastError();
5066 else if (!(request
->hconnect
= WinHttpConnect( request
->hsession
, hostname
, uc
.nPort
, 0 )))
5068 err
= GetLastError();
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();
5082 WinHttpSetOption( request
->hrequest
, WINHTTP_OPTION_CONTEXT_VALUE
, &request
, sizeof(request
) );
5084 request
->state
= REQUEST_STATE_OPEN
;
5085 request
->verb
= verb
;
5088 LeaveCriticalSection( &request
->cs
);
5092 WinHttpCloseHandle( request
->hconnect
);
5093 request
->hconnect
= NULL
;
5097 LeaveCriticalSection( &request
->cs
);
5098 return HRESULT_FROM_WIN32( err
);
5101 static HRESULT WINAPI
winhttp_request_SetRequestHeader(
5102 IWinHttpRequest
*iface
,
5106 struct winhttp_request
*request
= impl_from_IWinHttpRequest( iface
);
5107 DWORD len
, err
= ERROR_SUCCESS
;
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
;
5120 if (request
->state
>= REQUEST_STATE_SENT
)
5122 err
= ERROR_WINHTTP_CANNOT_CALL_AFTER_SEND
;
5125 len
= lstrlenW( header
) + 4;
5126 if (value
) len
+= lstrlenW( value
);
5127 if (!(str
= malloc( (len
+ 1) * sizeof(WCHAR
) )))
5129 err
= ERROR_OUTOFMEMORY
;
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();
5141 LeaveCriticalSection( &request
->cs
);
5142 return HRESULT_FROM_WIN32( err
);
5145 static HRESULT WINAPI
winhttp_request_GetResponseHeader(
5146 IWinHttpRequest
*iface
,
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
;
5161 if (!header
|| !value
)
5163 err
= ERROR_INVALID_PARAMETER
;
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
;
5177 err
= ERROR_SUCCESS
;
5178 if (!WinHttpQueryHeaders( request
->hrequest
, WINHTTP_QUERY_CUSTOM
, header
, *value
, &size
, NULL
))
5180 err
= GetLastError();
5181 SysFreeString( *value
);
5184 LeaveCriticalSection( &request
->cs
);
5185 return HRESULT_FROM_WIN32( err
);
5188 static HRESULT WINAPI
winhttp_request_GetAllResponseHeaders(
5189 IWinHttpRequest
*iface
,
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
;
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
;
5216 err
= ERROR_SUCCESS
;
5217 if (!WinHttpQueryHeaders( request
->hrequest
, WINHTTP_QUERY_RAW_HEADERS_CRLF
, NULL
, *headers
, &size
, NULL
))
5219 err
= GetLastError();
5220 SysFreeString( *headers
);
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
;
5233 case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE
:
5234 request
->bytes_available
= *(DWORD
*)buffer
;
5235 request
->error
= ERROR_SUCCESS
;
5237 case WINHTTP_CALLBACK_STATUS_READ_COMPLETE
:
5238 request
->bytes_read
= size
;
5239 request
->error
= ERROR_SUCCESS
;
5241 case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR
:
5243 WINHTTP_ASYNC_RESULT
*result
= (WINHTTP_ASYNC_RESULT
*)buffer
;
5244 request
->error
= result
->dwError
;
5248 request
->error
= ERROR_SUCCESS
;
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
};
5265 switch (WaitForMultipleObjects( 2, handles
, FALSE
, INFINITE
))
5268 ret
= request
->error
;
5270 case WAIT_OBJECT_0
+ 1:
5271 ret
= request
->error
= ERROR_CANCELLED
;
5272 SetEvent( request
->done
);
5275 ret
= request
->error
= GetLastError();
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
;
5296 if (!(request
->buffer
= malloc( buflen
))) return E_OUTOFMEMORY
;
5297 request
->buffer
[0] = 0;
5301 wait_set_status_callback( request
, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE
);
5302 if (!WinHttpQueryDataAvailable( request
->hrequest
, &request
->bytes_available
))
5304 err
= GetLastError();
5307 if ((err
= wait_for_completion( request
))) goto error
;
5308 if (!request
->bytes_available
) break;
5309 size
+= request
->bytes_available
;
5313 while (buflen
< size
) buflen
*= 2;
5314 if (!(tmp
= realloc( request
->buffer
, buflen
)))
5316 err
= ERROR_OUTOFMEMORY
;
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();
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
;
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
)
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
;
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
)
5388 const WCHAR
*str
= V_BSTR( &request
->data
);
5389 int i
, len
= lstrlenW( str
);
5391 for (i
= 0; i
< len
; i
++)
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
);
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();
5422 if ((err
= wait_for_completion( request
))) goto error
;
5423 if (sa
) SafeArrayUnaccessData( sa
);
5425 request
->state
= REQUEST_STATE_SENT
;
5429 if (sa
) SafeArrayUnaccessData( sa
);
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
;
5447 LeaveCriticalSection( &request
->cs
);
5448 while ((err
= MsgWaitForMultipleObjects( 1, &done
, FALSE
, timeout
, QS_ALLINPUT
)) == WAIT_OBJECT_0
+ 1)
5451 while (PeekMessageW( &msg
, NULL
, 0, 0, PM_REMOVE
))
5453 TranslateMessage( &msg
);
5454 DispatchMessageW( &msg
);
5460 ret
= request
->error
;
5463 ret
= ERROR_TIMEOUT
;
5466 ret
= GetLastError();
5469 EnterCriticalSection( &request
->cs
);
5470 if (err
== WAIT_OBJECT_0
) request
->proc_running
= FALSE
;
5474 static HRESULT WINAPI
winhttp_request_Send(
5475 IWinHttpRequest
*iface
,
5478 struct winhttp_request
*request
= impl_from_IWinHttpRequest( iface
);
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
);
5494 VariantClear( &request
->data
);
5495 if ((hr
= VariantCopyInd( &request
->data
, &body
)) != S_OK
)
5497 LeaveCriticalSection( &request
->cs
);
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
);
5514 static HRESULT WINAPI
winhttp_request_get_Status(
5515 IWinHttpRequest
*iface
,
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
;
5531 flags
= WINHTTP_QUERY_STATUS_CODE
| WINHTTP_QUERY_FLAG_NUMBER
;
5532 if (!WinHttpQueryHeaders( request
->hrequest
, flags
, NULL
, &status_code
, &len
, &index
))
5534 err
= GetLastError();
5537 *status
= status_code
;
5540 LeaveCriticalSection( &request
->cs
);
5541 return HRESULT_FROM_WIN32( err
);
5544 static HRESULT WINAPI
winhttp_request_get_StatusText(
5545 IWinHttpRequest
*iface
,
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
;
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
;
5572 err
= ERROR_SUCCESS
;
5573 if (!WinHttpQueryHeaders( request
->hrequest
, WINHTTP_QUERY_STATUS_TEXT
, NULL
, *status
, &len
, &index
))
5575 err
= GetLastError();
5576 SysFreeString( *status
);
5579 LeaveCriticalSection( &request
->cs
);
5580 return HRESULT_FROM_WIN32( err
);
5583 static DWORD
request_get_codepage( struct winhttp_request
*request
, UINT
*codepage
)
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
))
5596 return GetLastError();
5598 if ((p
= wcsstr( buffer
, L
"charset" )))
5600 p
+= lstrlenW( L
"charset" );
5601 while (*p
== ' ') p
++;
5604 while (*p
== ' ') p
++;
5605 if (!wcsicmp( p
, L
"utf-8" )) *codepage
= CP_UTF8
;
5610 return ERROR_SUCCESS
;
5613 static HRESULT WINAPI
winhttp_request_get_ResponseText(
5614 IWinHttpRequest
*iface
,
5617 struct winhttp_request
*request
= impl_from_IWinHttpRequest( iface
);
5619 DWORD err
= ERROR_SUCCESS
;
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
;
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
;
5639 MultiByteToWideChar( codepage
, 0, request
->buffer
, request
->offset
, *body
, len
);
5643 LeaveCriticalSection( &request
->cs
);
5644 return HRESULT_FROM_WIN32( err
);
5647 static HRESULT WINAPI
winhttp_request_get_ResponseBody(
5648 IWinHttpRequest
*iface
,
5651 struct winhttp_request
*request
= impl_from_IWinHttpRequest( iface
);
5654 DWORD err
= ERROR_SUCCESS
;
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
;
5667 if (!(sa
= SafeArrayCreateVector( VT_UI1
, 0, request
->offset
)))
5669 err
= ERROR_OUTOFMEMORY
;
5672 if ((hr
= SafeArrayAccessData( sa
, (void **)&ptr
)) != S_OK
)
5674 SafeArrayDestroy( sa
);
5675 LeaveCriticalSection( &request
->cs
);
5678 memcpy( ptr
, request
->buffer
, request
->offset
);
5679 if ((hr
= SafeArrayUnaccessData( sa
)) != S_OK
)
5681 SafeArrayDestroy( sa
);
5682 LeaveCriticalSection( &request
->cs
);
5685 V_VT( body
) = VT_ARRAY
|VT_UI1
;
5686 V_ARRAY( body
) = sa
;
5689 LeaveCriticalSection( &request
->cs
);
5690 return HRESULT_FROM_WIN32( err
);
5695 IStream IStream_iface
;
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
))
5718 FIXME("interface %s not implemented\n", debugstr_guid(riid
));
5719 return E_NOINTERFACE
;
5721 IStream_AddRef( iface
);
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
);
5737 free( stream
->data
);
5743 static HRESULT WINAPI
stream_Read( IStream
*iface
, void *buf
, ULONG len
, ULONG
*read
)
5745 struct stream
*stream
= impl_from_IStream( iface
);
5748 if (stream
->pos
.QuadPart
>= stream
->size
.QuadPart
)
5754 size
= min( stream
->size
.QuadPart
- stream
->pos
.QuadPart
, len
);
5755 memcpy( buf
, stream
->data
+ stream
->pos
.QuadPart
, size
);
5756 stream
->pos
.QuadPart
+= size
;
5762 static HRESULT WINAPI
stream_Write( IStream
*iface
, const void *buf
, ULONG len
, ULONG
*written
)
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
;
5783 static HRESULT WINAPI
stream_SetSize( IStream
*iface
, ULARGE_INTEGER newsize
)
5789 static HRESULT WINAPI
stream_CopyTo( IStream
*iface
, IStream
*stream
, ULARGE_INTEGER len
, ULARGE_INTEGER
*read
,
5790 ULARGE_INTEGER
*written
)
5796 static HRESULT WINAPI
stream_Commit( IStream
*iface
, DWORD flags
)
5802 static HRESULT WINAPI
stream_Revert( IStream
*iface
)
5808 static HRESULT WINAPI
stream_LockRegion( IStream
*iface
, ULARGE_INTEGER offset
, ULARGE_INTEGER len
, DWORD locktype
)
5814 static HRESULT WINAPI
stream_UnlockRegion( IStream
*iface
, ULARGE_INTEGER offset
, ULARGE_INTEGER len
, DWORD locktype
)
5820 static HRESULT WINAPI
stream_Stat( IStream
*iface
, STATSTG
*stg
, DWORD flag
)
5826 static HRESULT WINAPI
stream_Clone( IStream
*iface
, IStream
**stream
)
5832 static const IStreamVtbl stream_vtbl
=
5834 stream_QueryInterface
,
5845 stream_UnlockRegion
,
5850 static HRESULT WINAPI
winhttp_request_get_ResponseStream(
5851 IWinHttpRequest
*iface
,
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
;
5868 if (!(stream
= malloc( sizeof(*stream
) )))
5870 err
= ERROR_OUTOFMEMORY
;
5873 stream
->IStream_iface
.lpVtbl
= &stream_vtbl
;
5875 if (!(stream
->data
= malloc( request
->offset
)))
5878 err
= ERROR_OUTOFMEMORY
;
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
;
5888 LeaveCriticalSection( &request
->cs
);
5889 return HRESULT_FROM_WIN32( err
);
5892 static HRESULT WINAPI
winhttp_request_get_Option(
5893 IWinHttpRequest
*iface
,
5894 WinHttpRequestOption option
,
5897 struct winhttp_request
*request
= impl_from_IWinHttpRequest( iface
);
5900 TRACE("%p, %u, %p\n", request
, option
, value
);
5902 EnterCriticalSection( &request
->cs
);
5905 case WinHttpRequestOption_URLCodePage
:
5906 V_VT( value
) = VT_I4
;
5907 V_I4( value
) = request
->url_codepage
;
5910 FIXME("unimplemented option %u\n", option
);
5914 LeaveCriticalSection( &request
->cs
);
5918 static HRESULT WINAPI
winhttp_request_put_Option(
5919 IWinHttpRequest
*iface
,
5920 WinHttpRequestOption option
,
5923 struct winhttp_request
*request
= impl_from_IWinHttpRequest( iface
);
5926 TRACE("%p, %u, %s\n", request
, option
, debugstr_variant(&value
));
5928 EnterCriticalSection( &request
->cs
);
5931 case WinHttpRequestOption_EnableRedirects
:
5933 if (V_BOOL( &value
))
5934 request
->disable_feature
&= ~WINHTTP_DISABLE_REDIRECTS
;
5936 request
->disable_feature
|= WINHTTP_DISABLE_REDIRECTS
;
5939 case WinHttpRequestOption_URLCodePage
:
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
;
5956 FIXME("URL codepage %s is not recognized\n", debugstr_variant( &value
));
5960 FIXME("unimplemented option %u\n", option
);
5964 LeaveCriticalSection( &request
->cs
);
5968 static HRESULT WINAPI
winhttp_request_WaitForResponse(
5969 IWinHttpRequest
*iface
,
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
);
5984 switch ((err
= request_wait( request
, msecs
)))
5987 if (succeeded
) *succeeded
= VARIANT_FALSE
;
5988 err
= ERROR_SUCCESS
;
5992 if (succeeded
) *succeeded
= VARIANT_TRUE
;
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
);
6012 static HRESULT WINAPI
winhttp_request_SetTimeouts(
6013 IWinHttpRequest
*iface
,
6014 LONG resolve_timeout
,
6015 LONG connect_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
);
6032 static HRESULT WINAPI
winhttp_request_SetClientCertificate(
6033 IWinHttpRequest
*iface
,
6040 static HRESULT WINAPI
winhttp_request_SetAutoLogonPolicy(
6041 IWinHttpRequest
*iface
,
6042 WinHttpRequestAutoLogonPolicy policy
)
6044 struct winhttp_request
*request
= impl_from_IWinHttpRequest( iface
);
6047 TRACE("%p, %u\n", request
, policy
);
6049 EnterCriticalSection( &request
->cs
);
6052 case AutoLogonPolicy_Always
:
6053 request
->logon_policy
= WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW
;
6055 case AutoLogonPolicy_OnlyIfBypassProxy
:
6056 request
->logon_policy
= WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM
;
6058 case AutoLogonPolicy_Never
:
6059 request
->logon_policy
= WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH
;
6061 default: hr
= E_INVALIDARG
;
6064 LeaveCriticalSection( &request
->cs
);
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
;
6104 if (!(request
= calloc( 1, sizeof(*request
) ))) return E_OUTOFMEMORY
;
6105 request
->IWinHttpRequest_iface
.lpVtbl
= &winhttp_request_vtbl
;
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
);