Aracılığıyla paylaş


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 InternetWriteFileHttpEndRequest API dizisini HttpSendRequestExkullanan uygulamanız, çağrısınınHttpEndRequest FALSE GetLastError() döndürdüğünü ve 12032 - ERROR_INTERNET_FORCE_RETRYhata kodunu göstereceğini görür.

Çözüm

Bu sorunun çözümü, aynı işlem dizisini yeniden denemektir, yani:

  1. HttpSendRequestEx
  2. InternetWriteFile
  3. 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 HttpEndRequestyanı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 HttpOpenRequestHttpSendRequest ö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