WinINet API'sini kullanarak dosyaları IIS WebDav dizinine yükleme
Bu makalede, WinINet API'sini kullanarak dosyaları Microsoft Internet Information Services(IIS) üzerinde barındırılan bir WebDav dizinine nasıl program aracılığıyla yükleyebileceğiniz açıklanır.
Özgün ürün sürümü: Internet Information Services
Özgün KB numarası: 2001156
Belirtiler
Uygulamanız, Bir Köprü Metni Aktarım Protokolü (HTTP) PUT
fiili göndermek için WinINet API'sini kullanarak dosyaları program aracılığıyla IIS'de barındırılan bir WebDav dizinine yüklüyor. Uygulamanızın IIS ve Windows Server'da düzgün çalıştığını, ancak WinINet uygulamanızda kod değişikliği yapılmamış olsa bile dosyaları IIS WebDav dizinine yükleyemeyebileceğini fark edeceksiniz.
Neden
Bu sorunun nedeni, IIS üzerinde çalışan WebDav için tasarım değişikliğinden kaynaklanır. IIS üzerinde çalışan WebDav artık kimlik doğrulaması gerektirir ve yalnızca Anonim Kimlik Doğrulaması kullanılırsa çalışmaz. Bu nedenle, wininet InternetWriteFile
HttpEndRequest
API dizisini HttpSendRequestEx
kullanan uygulamanız, çağrısınınHttpEndRequest
FALSE GetLastError()
döndürdüğünü ve 12032 - ERROR_INTERNET_FORCE_RETRY
hata kodunu göstereceğini görür.
Çözüm
Bu sorunun çözümü, aynı işlem dizisini yeniden denemektir, yani:
HttpSendRequestEx
InternetWriteFile
HttpEndRequest
False döndürmeyen ve GetLastError()
12032'yi geri bildirmeyene kadar HttpEndRequest
(veya başka bir hata var). IIS'ye yanlış kimlik doğrulama bilgileri sunulursa, IIS her yeniden deneme için bir HTTP hatası 401 döndürmeye devam eder. Bu nedenle işlevin 12032 hatasını HttpEndRequest
kaç kez döndürdüğüne dikkat etmeniz ve sonsuz bir döngüye girmesini engellemeniz gerekir.
Windows NTLM Kimlik Doğrulaması varsa, HttpEndRequest
üç yönlü NTLM el sıkışmasını karşılamak için en fazla iki kez 12032 hatasını döndürür. İlk hata 12032 sunucudan bir HTTP hatası 401 yanıtı gösterir ve ikinci hata 12032 sunucudan Type-2 NTLM el sıkışma iletisini gösterir. Bu ileti, geçerli kimlik doğrulama bilgileri IIS'ye geçirilirse kullanıcının kimliği doğrulanır ve karşıya yükleme başarılı olur.
Yukarıdaki işlevleri döngüde çağırmak için bir yeniden deneme mantığı kullandığınızda, çağrısının birden çok kez yapıldığına InternetWriteFile
dikkat edin. Bunun anlamı, çağrısının InternetWriteFile
verileri ağ üzerinden yazacağı ve bunun bant genişliği kaybına neden olacağıdır. Bunun olmasını önlemek için sunucuya sahte bir HTTP HEAD
isteği gönderebilirsiniz. Bu istek, isteğin ön kimliğini doğrular ve daha sonra çağrıldığında InternetWriteFile
HTTP Yükünü göndermeme çağrısına HttpSendRequest
neden olur. Ağ İzleyicisi veya WinINet günlüğü hakkında bilginiz varsa, sunucuya gönderilen ilk PUT
isteğin content-Length değeri sıfır olur ve bu da yük aktarımını engeller ve NTLM el sıkışması tamamlanana kadar yük aktarılamaz.
WebDav özelliği, Windows Kimlik Doğrulamasını kullanmanızı gerektirmez; WebDav sunucunuzu SSL üzerinden Temel Kimlik Doğrulaması kullanacak şekilde yapılandırabilirsiniz; bu da verilerin karşıya yüklenmesinin güvenliğini sağlar. Temel kimlik doğrulaması yapılandırıldığında giden isteğe doğrudan geçerli bir base-64 kodlamalı kullanıcı adı parola dizesi ekleyebilirsiniz. Bu, IIS'nin HTTP hatası 401'i geri döndürmesini önler ve bu nedenle HttpEndRequest
12032 hatasını geri döndürmez. WinINet API'sini çağırarak Giden isteğe Temel kimlik doğrulama bilgilerini ekleyebilirsiniz:
HttpAddRequestHeaders(hRequest, "Authorization: Basic <<valid base-64 encoded username:password string>>\r\n", -1, HTTP_ADDREQ_FLAG_ADD);
Giden HTTP isteğine Yetkilendirme üst bilgisini doğrudan eklemek için aramadan HttpSendRequestEx
önce bunu yapın.
Aşağıdaki kod örneği, 12032'den HttpEndRequest
yanıt döndürme hatasını işlemek için yeniden deneme mantığını nasıl kullanabileceğinizi gösterir. Bu örnek, yukarıda açıklanan ön kimlik doğrulama isteğini kapsamaz. Ön kimlik doğrulaması yapmak için yapmanız gereken tek şey, aşağıdaki kodu çağırmadan HttpOpenRequest
HttpSendRequest
önce hedef sunucuya HTTP HEAD
fiiliyle çağrısı HttpSendRequestEx
yapmaktır.
Kod örneği
BOOL UseHttpSendReqEx(HINTERNET hRequest, DWORD dwPostSize)
{
INTERNET_BUFFERS BufferIn;
DWORD dwBytesWritten;
int iChunkCtr;
BYTE pBuffer[1024];
BOOL bRet;
BufferIn.dwStructSize = sizeof( INTERNET_BUFFERS ); // Must be set or you will get an error
BufferIn.Next = NULL;
BufferIn.lpcszHeader = NULL;
BufferIn.dwHeadersLength = 0;
BufferIn.dwHeadersTotal = 0;
BufferIn.lpvBuffer = NULL;
BufferIn.dwBufferLength = 0;
BufferIn.dwBufferTotal = dwPostSize; // This is the only member used other than dwStructSize
BufferIn.dwOffsetLow = 0;
BufferIn.dwOffsetHigh = 0;
// The following variable will keep track of the number of times HttpSendRequestEx is called
int iNumTrials = 0;
bool bRetVal = FALSE;
// The retry goto is to re-try the operation when HttpEndRequest returns error 12032.
while(1)
{
if(!HttpSendRequestEx( hRequest, &BufferIn, NULL, 0, 0))
{
printf( "Error on HttpSendRequestEx %d\n",GetLastError());
return FALSE;
}
FillMemory(pBuffer, 1024, 'D'); // Fill buffer with data
bRet=TRUE;
for(iChunkCtr=1; iChunkCtr<=(int)dwPostSize/1024 && bRet; iChunkCtr++)
{
dwBytesWritten = 0;
if(bRet=InternetWriteFile( hRequest, pBuffer, 1024, &dwBytesWritten))
printf( "\r%d bytes sent.", iChunkCtr*1024);
}
if(!bRet)
{
printf( "\nError on InternetWriteFile %lu\n",GetLastError());
return FALSE;
}
if(!HttpEndRequest(hRequest, NULL, 0, 0))
{
int iLastError = GetLastError();
printf( "Error on HttpEndRequest %lu \n", iLastError);
// Use the following logic to "retry" after receiving error 12032 from HttpEndRequest
//
// Error 12032 = ERROR_INTERNET_FORCE_RETRY means that you just need to send the request again
//
// Sending request again means that you simply need to call:
//
// HttpSendRequest, InternetWriteFile, HttpEndRequest until HttpEndRequest does not return
// back error 12032.
//
// Since NTLM is a 3-way handshake protocol, it will happen that HttpEndRequest will return
// error 12032 two times and hence the following check.
//
// If error 12032 is returned 3 or more number of times, then there is some Other error.
if(iLastError == 12032 && iNumTrials < 3) {
iNumTrials++;
continue; // This will retry HttpSendRequestEx...
}
return FALSE;
}
return TRUE;
}
}
Daha fazla bilgi
Geri Bildirim
https://aka.ms/ContentUserFeedback.
Çok yakında: 2024 boyunca, içerik için geri bildirim mekanizması olarak GitHub Sorunları’nı kullanımdan kaldıracak ve yeni bir geri bildirim sistemiyle değiştireceğiz. Daha fazla bilgi için bkz.Gönderin ve geri bildirimi görüntüleyin