コード例 #1
0
ファイル: CurlHttp.cs プロジェクト: dw4dev/Phalanger
        /// <summary>
        /// Execute is modified Curl_http() which in Curl gets called from the generic Curl_do() function when a HTTP
        /// request is to be performed. This creates and sends a properly constructed
        /// HTTP request.
        /// </summary>
        /// <param name="curl"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        internal override object Execute(PhpCurlResource curl, ref CURLcode result)
        {
            UserDefined data = curl.Data;
            HttpBitsUploader uploader;
            bool terminatedCorrectly = false;
            int redirectAttempts = 0;
            bool keepVerb = false;

            result = CURLcode.CURLE_OK;

            if (data.Str[(int)DupString.SET_URL] == null)
            {
                result = CURLcode.CURLE_COULDNT_CONNECT;
                return false;
            }

            Uri uri = Utils.CompleteUri(PhpVariable.AsString(data.Str[(int)DupString.SET_URL]),
                                            Scheme,
                                            data.UsePort);


            for (; ; )
            {
                request = (HttpWebRequest)HttpWebRequest.Create(uri);

                Curl_HttpReq httpreq = (redirectAttempts == 0) || keepVerb ? setRequestMethod(data) : Curl_HttpReq.GET;
                setTimeOut(data);
                setHttpVersion(data);

                request.AllowAutoRedirect = data.FollowLocation;
                request.MaximumAutomaticRedirections = data.MaxRedirects;

                if (data.Str[(int)DupString.USERAGENT] != null)
                    request.UserAgent = PhpVariable.AsString(data.Str[(int)DupString.USERAGENT]);

                if (data.Str[(int)DupString.SET_REFERER] != null)
                    request.Referer = PhpVariable.AsString(data.Str[(int)DupString.SET_REFERER]);

                if (data.Headers != null)
                    request.SetHttpHeaders(data.Headers);

                setProxy(data);
                setCredentials(data);
                setCookies(data);

                //ssl.VerifyPeer && ssl.VerifyHost == 2 is supported by default .NET
                // other values are currently unsupported

                if (data.Str[(int)DupString.CERT] != null)
                {
                    X509Certificate cert;
                    string certPath;

                    try
                    {
                        certPath = Path.Combine(ScriptContext.CurrentContext.WorkingDirectory, PhpVariable.AsString(data.Str[(int)DupString.SSL_CAFILE]));

                        if (data.Str[(int)DupString.KEY_PASSWD] == null)
                            cert = new X509Certificate(certPath);
                        else
                            cert = new X509Certificate(certPath, PhpVariable.AsString(data.Str[(int)DupString.KEY_PASSWD]));

                        request.ClientCertificates.Add(cert);
                    }
                    catch (CryptographicException)
                    {
                        //TODO: here are more caises to differentiate
                        result = CURLcode.CURLE_SSL_CACERT_BADFILE;
                        return false;
                    }

                }


                switch (httpreq)
                {
                    case Curl_HttpReq.POST_FORM:

                        //same as POST but we can send multiple items asform-data


                        if (data.HttpPostForm != null)
                        {
                            try
                            {
                                HttpFormDataUploader formUploader = new HttpFormDataUploader(request);
                                formUploader.UploadForm(data.HttpPostForm);
                            }
                            catch (WebException ex)
                            {
                                switch (ex.Status)
                                {
                                    case WebExceptionStatus.Timeout:
                                        result = CURLcode.CURLE_OPERATION_TIMEOUTED;
                                        break;
                                    default:
                                        result = CURLcode.CURLE_COULDNT_CONNECT;// for now just this
                                        break;

                                }
                                return false;
                            }
                        }

                        break;


                    case Curl_HttpReq.PUT: /* Let's PUT the data to the server! */

                        //INFILE & INFILESIZE has to be set

                        NativeStream nativeStream = data.Infile as NativeStream;

                        if (nativeStream == null)
                            return false;

                        FileStream fs = nativeStream.RawStream as FileStream;

                        if (fs == null)
                            return false;

                        try
                        {
                            uploader = new HttpBitsUploader(request);
                            uploader.UploadFile(fs);
                        }
                        catch (WebException ex)
                        {
                            switch (ex.Status)
                            {
                                case WebExceptionStatus.Timeout:
                                    result = CURLcode.CURLE_OPERATION_TIMEOUTED;
                                    break;
                                default:
                                    result = CURLcode.CURLE_COULDNT_CONNECT;// for now just this
                                    break;

                            }
                            return false;
                        }

                        break;

                    case Curl_HttpReq.POST:
                        /* this is the simple POST, using x-www-form-urlencoded style */

                        if (String.IsNullOrEmpty(request.ContentType))// if Content-type isn't set set the default
                            request.ContentType = "application/x-www-form-urlencoded";

                        if (data.Postfields != null)
                        {
                            try
                            {
                                uploader = new HttpBitsUploader(request);
                                uploader.UploadData(data.Postfields);
                            }
                            catch (WebException ex)
                            {
                                switch (ex.Status)
                                {
                                    case WebExceptionStatus.Timeout:
                                        result = CURLcode.CURLE_OPERATION_TIMEOUTED;
                                        break;
                                    default:
                                        result = CURLcode.CURLE_COULDNT_CONNECT;// for now just this
                                        break;

                                }
                                return false;
                            }
                        }

                        break;
                }

                try
                {
                    // if we got this far, we will turn off AutoRedirect (assuming it was on), since
                    // we are ready to handle manually following certain responses. this is needed
                    // to harvest cookies that are set on any intermediate response (i.e. anything
                    // other than the last one followed), since the .NET HTTP class will use, but
                    // NOT return, cookies set on anything but the last request.
                    request.AllowAutoRedirect = false;
                    response = (HttpWebResponse)request.GetResponse();
                }
                catch (WebException ex)
                {
                    switch (ex.Status)
                    {
                        case WebExceptionStatus.Timeout:
                            result = CURLcode.CURLE_OPERATION_TIMEOUTED;
                            break;
                        case WebExceptionStatus.ConnectFailure:
                            result = CURLcode.CURLE_COULDNT_CONNECT;
                            break;
                        case WebExceptionStatus.TrustFailure:
                            result = CURLcode.CURLE_SSL_CACERT;
                            break;
                        case WebExceptionStatus.ProtocolError:
                            //Response from server was complete, but indicated protocol error as 404, 401 etc.
                            break;
                        default:
                            result = CURLcode.CURLE_COULDNT_CONNECT;// for now just this
                            break;

                    }
                    //TODO: other errorCodes

                    response = (HttpWebResponse)ex.Response;
                    //return false;
                    //error = true;
                }

                if (response == null)// just to make sure I have the response object
                    return false;


                if (data.FollowLocation)
                {
                    // see if we need to follow a redirect.
                    switch (response.StatusCode)
                    {
                        case HttpStatusCode.MovedPermanently:
                        case HttpStatusCode.Found:
                        case HttpStatusCode.SeeOther:
                        case HttpStatusCode.RedirectKeepVerb:
                            if (redirectAttempts++ >= data.MaxRedirects)
                            {
                                result = CURLcode.CURLE_TOO_MANY_REDIRECTS;
                                return false;
                            }
                            string location = response.Headers["Location"];
                            if (!string.IsNullOrWhiteSpace(location))
                            {
                                try
                                {
                                    keepVerb = response.StatusCode == HttpStatusCode.RedirectKeepVerb;
                                    data.Cookies.Add(response.Cookies);
                                    response.Close();
                                    uri = new Uri(uri, location);
                                    continue;
                                }
                                catch (Exception)
                                {
                                    // closest error code though could be confusing as it's not the user-
                                    // submitted URL that's the problem
                                    result = CURLcode.CURLE_URL_MALFORMAT;
                                    return false;
                                }
                            }
                            break;
                    }
                }

                //Save cookies
                data.Cookies.Add(response.Cookies);
                // break out of the for loop as we aren't following a redirect
                break;
            }

            byte[] headers = null;
            byte[] content = null;
            int headersLength = 0;

            if (data.IncludeHeader)
            {
                //It's necessary to put HTTP header into the result

                //first we need to create it since there isn't anywhere
                headers = Encoding.ASCII.GetBytes(response.GetHttpHeaderAsString());
                headersLength = headers.Length;
            }

            if (data.FunctionWriteHeader != null)// TODO: probably invoke before
            {
                response.InvokeHeaderFunction(curl, data.FunctionWriteHeader);
            }

            Stream writeStream = null;

            if (data.WriteFunction != null)
            {
                writeStream = new WriteFunctionStream(curl, data.WriteFunction);
            }
            else if (data.OutFile != null)
            {
                var outStream = data.OutFile as PhpStream;

                if (outStream == null)
                    return false;

                Stream fs = outStream.RawStream as Stream;

                if (fs == null)
                    return false;

                writeStream = fs;
            }
            else if (data.ReturnTransfer == false) // Output to standart output
            {
                writeStream = ScriptContext.CurrentContext.OutputStream;
            }


            if (writeStream != null)
            {
                if (headers != null) //there is http header to copy to the result
                {
                    writeStream.Write(headers, 0, headersLength);
                }

                HttpBitsDownloader reader = new HttpBitsDownloader(response);

                try
                {
                    reader.ReadToStream(writeStream, out terminatedCorrectly);
                }
                catch (WebException ex)
                {
                    switch (ex.Status)
                    {
                        case WebExceptionStatus.Timeout:
                            result = CURLcode.CURLE_OPERATION_TIMEOUTED;
                            break;
                        default:
                            result = CURLcode.CURLE_COULDNT_CONNECT;// for now just this
                            break;
                    }
                }

                if (!terminatedCorrectly)
                    result = CURLcode.CURLE_PARTIAL_FILE;

                return true;
            }
            else
            {
                // Read the response
                HttpBitsDownloader reader = new HttpBitsDownloader(response);

                try
                {
                    content = reader.ReadToEnd(headersLength, out terminatedCorrectly);
                }
                catch(WebException ex)
                {
                    switch (ex.Status)
                    {
                        case WebExceptionStatus.Timeout:
                            result = CURLcode.CURLE_OPERATION_TIMEOUTED;
                            break;
                        default:
                            result = CURLcode.CURLE_COULDNT_CONNECT;// for now just this
                            break;
                    }
                }

                if (!terminatedCorrectly)
                    result = CURLcode.CURLE_PARTIAL_FILE;

                if (headers != null) //there is http header to copy to the result
                {
                    if (content != null)
                        Buffer.BlockCopy(headers, 0, content, 0, headersLength);
                    else
                        content = headers;
                }

                if (content == null)
                    return PhpBytes.Empty;
                else
                    return new PhpBytes(content);
            }

        }