motion/http/query.rb in bubble-wrap-1.3.0 vs motion/http/query.rb in bubble-wrap-1.4.0

- old
+ new

@@ -1,7 +1,7 @@ # Class wrapping NSConnection and often used indirectly by the BubbleWrap::HTTP module methods. -class BubbleWrap::HTTP::Query +module BubbleWrap; module HTTP; class Query attr_accessor :request attr_accessor :connection attr_accessor :credentials # username & password has a hash attr_accessor :proxy_credential # credential supplied to proxy servers attr_accessor :post_data @@ -18,21 +18,22 @@ # http_method<Symbol>:: Value representing the HTTP method to use # options<Hash>:: optional options used for the query # # ==== Options # :payload<String> - data to pass to a POST, PUT, DELETE query. - # :delegator - Proc, class or object to call when the file is downloaded. + # :action - Proc, class or object to call when the file is downloaded. # a proc will receive a Response object while the passed object # will receive the handle_query_response method # :headers<Hash> - headers send with the request # :cookies<Boolean> - Set whether cookies should be sent with request or not (Default: true) # Anything else will be available via the options attribute reader. # def initialize(url_string, http_method = :get, options={}) @method = http_method.upcase.to_s @delegator = options.delete(:action) || self @payload = options.delete(:payload) + @encoding = options.delete(:encoding) || NSUTF8StringEncoding @files = options.delete(:files) @boundary = options.delete(:boundary) || BW.create_uuid @credentials = options.delete(:credentials) || {} @credentials = {:username => nil, :password => nil}.merge(@credentials) @timeout = options.delete(:timeout) || 30.0 @@ -68,13 +69,11 @@ # NSURLConnection, even if the connection has not yet started. The `response` # object will be a NSURLResponse, *not* an `NSHTTPURLResponse`, and so will start to crash. if App.osx? && !response.is_a?(NSHTTPURLResponse) return end - @status_code = response.statusCode - @response_headers = response.allHeaderFields - @response_size = response.expectedContentLength.to_f + did_receive_response(response) end # This delegate method get called every time a chunk of data is being received def connection(connection, didReceiveData:received_data) @received_data ||= NSMutableData.new @@ -93,11 +92,15 @@ @redirect_count ||= 0 @redirect_count += 1 log "##{@redirect_count} HTTP redirect_count: #{request.inspect} - #{self.description}" if @redirect_count >= 30 - @response.error_message = "Too many redirections" + @response.error = NSError.errorWithDomain('BubbleWrap::HTTP', code:NSURLErrorHTTPTooManyRedirects, + userInfo:NSDictionary.dictionaryWithObject("Too many redirections", + forKey: NSLocalizedDescriptionKey)) + @response.error_message = @response.error.localizedDescription + show_status_indicator false @request.done_loading! call_delegator_with_response nil else @url = request.URL if @follow_urls @@ -107,10 +110,11 @@ def connection(connection, didFailWithError: error) log "HTTP Connection to #{@url.absoluteString} failed #{error.localizedDescription}" show_status_indicator false @request.done_loading! + @response.error = error @response.error_message = error.localizedDescription call_delegator_with_response end def connection(connection, didSendBodyData:sending, totalBytesWritten:written, totalBytesExpectedToWrite:expected) @@ -137,19 +141,31 @@ new_credential = NSURLCredential.credentialWithUser(credentials[:username], password:credentials[:password], persistence:@credential_persistence) challenge.sender.useCredential(new_credential, forAuthenticationChallenge:challenge) log "auth challenged, answered with credentials: #{credentials.inspect}" end else + did_receive_response(challenge.failureResponse) + @response.update(status_code: status_code, headers: response_headers, url: @url, original_url: @original_url) challenge.sender.cancelAuthenticationChallenge(challenge) log 'Auth Failed :(' end end + def cancel + @connection.cancel + show_status_indicator false + @request.done_loading! + end private - private + def did_receive_response(response) + @status_code = response.statusCode + @response_headers = response.allHeaderFields + @response_size = response.expectedContentLength.to_f + end + def show_status_indicator(show) if App.ios? UIApplication.sharedApplication.networkActivityIndicatorVisible = show end end @@ -171,11 +187,11 @@ request end def set_content_type return if headers_provided? - return if (@method == "GET" || @method == "HEAD") + return if (@method == "GET" || @method == "HEAD" || @method == "OPTIONS") @headers ||= {} @headers["Content-Type"] = case @format when :json "application/json" when :xml @@ -198,11 +214,11 @@ def credentials_provided? @credentials[:username] && @credentials[:password] end def create_request_body - return nil if (@method == "GET" || @method == "HEAD") + return nil if (@method == "GET" || @method == "HEAD" || @method == "OPTIONS") return nil unless (@payload || @files) body = NSMutableData.data append_payload(body) if @payload @@ -215,11 +231,14 @@ def append_payload(body) if @payload.is_a?(NSData) body.appendData(@payload) elsif @payload.is_a?(String) - body.appendData(@payload.dataUsingEncoding NSUTF8StringEncoding) + body.appendData(@payload.to_encoded_data @encoding) + elsif @format == :json + json_string = BW::JSON.generate(@payload) + body.appendData(json_string.to_encoded_data @encoding) else append_form_params(body) end body end @@ -229,11 +248,11 @@ list.each do |key, value| s = "--#{@boundary}\r\n" s += "Content-Disposition: form-data; name=\"#{key}\"\r\n\r\n" s += value.to_s s += "\r\n" - body.appendData(s.dataUsingEncoding NSUTF8StringEncoding) + body.appendData(s.to_encoded_data @encoding) end @payload_or_files_were_appended = true body end @@ -249,11 +268,11 @@ end end def parse_file(key, value) if value.is_a?(Hash) - raise InvalidFileError if value[:data].nil? + raise(InvalidFileError, "You need to supply a `:data` entry in #{value} for file '#{key}' in your HTTP `:files`") if value[:data].nil? {data: value[:data], filename: value[:filename] || key} else {data: value, filename: key} end end @@ -263,26 +282,26 @@ file = parse_file(key, value) s = "--#{@boundary}\r\n" s += "Content-Disposition: form-data; name=\"#{key}\"; filename=\"#{file[:filename]}\"\r\n" s += "Content-Type: application/octet-stream\r\n\r\n" file_data = NSMutableData.new - file_data.appendData(s.dataUsingEncoding NSUTF8StringEncoding) + file_data.appendData(s.to_encoded_data @encoding) file_data.appendData(file[:data]) - file_data.appendData("\r\n".dataUsingEncoding NSUTF8StringEncoding) + file_data.appendData("\r\n".to_encoded_data @encoding) body.appendData(file_data) end @payload_or_files_were_appended = true body end def append_body_boundary(body) - body.appendData("--#{@boundary}--\r\n".dataUsingEncoding NSUTF8StringEncoding) + body.appendData("--#{@boundary}--\r\n".to_encoded_data @encoding) end def create_url(url_string) url_string = url_string.stringByAddingPercentEscapesUsingEncoding NSUTF8StringEncoding - if (@method == "GET" || @method == "HEAD") && @payload + if (@method == "GET" || @method == "HEAD" || @method == "OPTIONS") && @payload unless @payload.empty? convert_payload_to_url if @payload.is_a?(Hash) url_string += "?#{@payload}" end end @@ -297,12 +316,13 @@ raise InvalidURLError, "Invalid URL provided (Make sure you include a valid URL scheme, e.g. http:// or similar)." end end def escape(string) - if string - CFURLCreateStringByAddingPercentEscapes nil, string.to_s, nil, "!*'();:@&=+$,/?%#[]", KCFStringEncodingUTF8 + string_to_escape = string.to_s + if string_to_escape + CFURLCreateStringByAddingPercentEscapes nil, string_to_escape, nil, "!*'();:@&=+$,/?%#[]", KCFStringEncodingUTF8 end end def convert_payload_to_url params_array = process_payload_hash(@payload) @@ -362,6 +382,6 @@ # This is a temporary method used for mocking. def create_connection(request, delegate) NSURLConnection.connectionWithRequest(request, delegate:delegate) end -end \ No newline at end of file +end; end; end