htty 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. data/MIT-LICENSE.rdoc +9 -0
  2. data/README.rdoc +199 -0
  3. data/VERSION +1 -0
  4. data/app/htty.rb +14 -0
  5. data/app/htty/cli.rb +77 -0
  6. data/app/htty/cli/command.rb +185 -0
  7. data/app/htty/cli/commands.rb +43 -0
  8. data/app/htty/cli/commands/address.rb +84 -0
  9. data/app/htty/cli/commands/body_clear.rb +20 -0
  10. data/app/htty/cli/commands/body_request.rb +51 -0
  11. data/app/htty/cli/commands/body_response.rb +59 -0
  12. data/app/htty/cli/commands/body_set.rb +58 -0
  13. data/app/htty/cli/commands/body_unset.rb +45 -0
  14. data/app/htty/cli/commands/cd.rb +20 -0
  15. data/app/htty/cli/commands/cookie_add.rb +20 -0
  16. data/app/htty/cli/commands/cookie_remove.rb +20 -0
  17. data/app/htty/cli/commands/cookies.rb +68 -0
  18. data/app/htty/cli/commands/cookies_add.rb +62 -0
  19. data/app/htty/cli/commands/cookies_clear.rb +20 -0
  20. data/app/htty/cli/commands/cookies_remove.rb +60 -0
  21. data/app/htty/cli/commands/cookies_remove_all.rb +50 -0
  22. data/app/htty/cli/commands/cookies_use.rb +57 -0
  23. data/app/htty/cli/commands/delete.rb +20 -0
  24. data/app/htty/cli/commands/exit.rb +20 -0
  25. data/app/htty/cli/commands/follow.rb +56 -0
  26. data/app/htty/cli/commands/form.rb +20 -0
  27. data/app/htty/cli/commands/form_add.rb +20 -0
  28. data/app/htty/cli/commands/form_clear.rb +26 -0
  29. data/app/htty/cli/commands/form_remove.rb +20 -0
  30. data/app/htty/cli/commands/form_remove_all.rb +20 -0
  31. data/app/htty/cli/commands/fragment_clear.rb +20 -0
  32. data/app/htty/cli/commands/fragment_set.rb +59 -0
  33. data/app/htty/cli/commands/fragment_unset.rb +48 -0
  34. data/app/htty/cli/commands/get.rb +20 -0
  35. data/app/htty/cli/commands/header_set.rb +20 -0
  36. data/app/htty/cli/commands/header_unset.rb +20 -0
  37. data/app/htty/cli/commands/headers_clear.rb +20 -0
  38. data/app/htty/cli/commands/headers_request.rb +70 -0
  39. data/app/htty/cli/commands/headers_response.rb +65 -0
  40. data/app/htty/cli/commands/headers_set.rb +57 -0
  41. data/app/htty/cli/commands/headers_unset.rb +54 -0
  42. data/app/htty/cli/commands/headers_unset_all.rb +48 -0
  43. data/app/htty/cli/commands/help.rb +100 -0
  44. data/app/htty/cli/commands/history.rb +60 -0
  45. data/app/htty/cli/commands/history_verbose.rb +81 -0
  46. data/app/htty/cli/commands/host_set.rb +59 -0
  47. data/app/htty/cli/commands/http_delete.rb +40 -0
  48. data/app/htty/cli/commands/http_get.rb +42 -0
  49. data/app/htty/cli/commands/http_head.rb +36 -0
  50. data/app/htty/cli/commands/http_options.rb +36 -0
  51. data/app/htty/cli/commands/http_post.rb +34 -0
  52. data/app/htty/cli/commands/http_put.rb +29 -0
  53. data/app/htty/cli/commands/http_trace.rb +36 -0
  54. data/app/htty/cli/commands/path_set.rb +54 -0
  55. data/app/htty/cli/commands/port_set.rb +55 -0
  56. data/app/htty/cli/commands/post.rb +20 -0
  57. data/app/htty/cli/commands/put.rb +20 -0
  58. data/app/htty/cli/commands/query_clear.rb +20 -0
  59. data/app/htty/cli/commands/query_set.rb +59 -0
  60. data/app/htty/cli/commands/query_unset.rb +56 -0
  61. data/app/htty/cli/commands/query_unset_all.rb +50 -0
  62. data/app/htty/cli/commands/quit.rb +24 -0
  63. data/app/htty/cli/commands/reuse.rb +74 -0
  64. data/app/htty/cli/commands/scheme_set.rb +69 -0
  65. data/app/htty/cli/commands/status.rb +52 -0
  66. data/app/htty/cli/commands/undo.rb +13 -0
  67. data/app/htty/cli/commands/userinfo_clear.rb +20 -0
  68. data/app/htty/cli/commands/userinfo_set.rb +56 -0
  69. data/app/htty/cli/commands/userinfo_unset.rb +47 -0
  70. data/app/htty/cli/cookie_clearing_command.rb +26 -0
  71. data/app/htty/cli/display.rb +182 -0
  72. data/app/htty/cli/http_method_command.rb +75 -0
  73. data/app/htty/cli/url_escaping.rb +27 -0
  74. data/app/htty/cookies_util.rb +36 -0
  75. data/app/htty/no_location_header_error.rb +12 -0
  76. data/app/htty/no_response_error.rb +12 -0
  77. data/app/htty/no_set_cookie_header_error.rb +13 -0
  78. data/app/htty/ordered_hash.rb +69 -0
  79. data/app/htty/payload.rb +48 -0
  80. data/app/htty/request.rb +471 -0
  81. data/app/htty/requests_util.rb +92 -0
  82. data/app/htty/response.rb +34 -0
  83. data/app/htty/session.rb +29 -0
  84. data/bin/htty +5 -0
  85. data/spec/unit/htty/cli_spec.rb +27 -0
  86. data/spec/unit/htty/ordered_hash_spec.rb +54 -0
  87. data/spec/unit/htty/request_spec.rb +1236 -0
  88. data/spec/unit/htty/response_spec.rb +0 -0
  89. data/spec/unit/htty/session_spec.rb +13 -0
  90. metadata +158 -0
@@ -0,0 +1,43 @@
1
+ # Loads constants defined within HTTY::CLI::Commands.
2
+
3
+ module HTTY; end
4
+
5
+ class HTTY::CLI; end
6
+
7
+ # Contains classes that implement commands in the user interface.
8
+ module HTTY::CLI::Commands
9
+
10
+ extend Enumerable
11
+
12
+ # Returns a HTTY::CLI::Command descendant whose command line representation
13
+ # matches the specified _command_line_. If an _attributes_ hash is specified,
14
+ # it is used to initialize the command.
15
+ def self.build_for(command_line, attributes={})
16
+ each do |klass|
17
+ if (command = klass.build_for(command_line, attributes))
18
+ return command
19
+ end
20
+ end
21
+ nil
22
+ end
23
+
24
+ # Yields each HTTY::CLI::Command descendant in turn.
25
+ def self.each
26
+ Dir.glob "#{File.dirname __FILE__}/commands/*.rb" do |f|
27
+ class_name = File.basename(f, '.rb').gsub(/^(.)/) do |initial|
28
+ initial.upcase
29
+ end.gsub(/_(\S)/) do |initial|
30
+ initial.gsub(/_/, '').upcase
31
+ end
32
+ klass = const_get(class_name) rescue nil
33
+ yield klass if klass
34
+ end
35
+ self
36
+ end
37
+
38
+ end
39
+
40
+ Dir.glob "#{File.dirname __FILE__}/commands/*.rb" do |f|
41
+ require File.expand_path("#{File.dirname __FILE__}/commands/" +
42
+ File.basename(f, '.rb'))
43
+ end
@@ -0,0 +1,84 @@
1
+ # Defines HTTY::CLI::Commands::Address.
2
+
3
+ require File.expand_path("#{File.dirname __FILE__}/../command")
4
+ require File.expand_path("#{File.dirname __FILE__}/../cookie_clearing_command")
5
+ require File.expand_path("#{File.dirname __FILE__}/port_set")
6
+ require File.expand_path("#{File.dirname __FILE__}/scheme_set")
7
+
8
+ module HTTY; end
9
+
10
+ class HTTY::CLI; end
11
+
12
+ module HTTY::CLI::Commands; end
13
+
14
+ # Encapsulates the _address_ command.
15
+ class HTTY::CLI::Commands::Address < HTTY::CLI::Command
16
+
17
+ include HTTY::CLI::CookieClearingCommand
18
+
19
+ # Returns the name of a category under which help for the _address_ command
20
+ # should appear.
21
+ def self.category
22
+ 'Navigation'
23
+ end
24
+
25
+ # Returns the arguments for the command-line usage of the _address_ command.
26
+ def self.command_line_arguments
27
+ 'address'
28
+ end
29
+
30
+ # Returns the help text for the _address_ command.
31
+ def self.help
32
+ 'Changes the address of the request'
33
+ end
34
+
35
+ # Returns the extended help text for the _address_ command.
36
+ def self.help_extended
37
+ 'Changes the address used for the request. Does not communicate with the ' +
38
+ "endpoint.\n" +
39
+ "\n" +
40
+ 'The URL you supply can be partial. At a minimum, you must specify a ' +
41
+ 'host. The optional and required elements of an address are illustrated ' +
42
+ "below:\n" +
43
+ "\n" +
44
+ "https://steve:woodside@apple.com:6666/store?q=ipad#sold-to-date\n" +
45
+ "\\______/\\_____________/\\_______/\\___/\\____/\\_____/\\___________/\n" +
46
+ " 1. 2. 3. 4. 5. 6. 7.\n" +
47
+ "\n" +
48
+ "1. A scheme, or protocol identifier (i.e., 'http://' or 'https://' -- " +
49
+ "optional)\n" +
50
+ "2. Userinfo (e.g., 'username:password@' -- optional)\n" +
51
+ "3. A host (i.e., a hostname or IP address -- required)\n" +
52
+ "4. A TCP port number (i.e., ':0' through ':65535' -- optional)\n" +
53
+ "5. A path (optional)\n" +
54
+ "6. A query string (e.g., '?foo=bar&baz=qux' -- optional)\n" +
55
+ "7. A fragment (e.g., '#fragment-name' -- optional)\n" +
56
+ "\n" +
57
+ 'If (1) is omitted, HTTP is used, except if (4) is specified as port ' +
58
+ "443, in which case HTTPS is used.\n" +
59
+ "\n" +
60
+ "If (3) is omitted, host 0.0.0.0 is used.\n" +
61
+ "\n" +
62
+ 'If (4) is omitted, port 80 is used, except if (1) is specified as ' +
63
+ "HTTPS, in which case port 443 is used.\n" +
64
+ "\n" +
65
+ "If (5) is omitted, the root path (i.e., \"/\") is used.\n" +
66
+ "\n" +
67
+ 'The console prompt shows the address for the current request.'
68
+ end
69
+
70
+ # Returns related command classes for the _address_ command.
71
+ def self.see_also_commands
72
+ [HTTY::CLI::Commands::SchemeSet, HTTY::CLI::Commands::PortSet]
73
+ end
74
+
75
+ # Performs the _address_ command.
76
+ def perform
77
+ add_request_if_has_response do |request|
78
+ notify_if_cookies_cleared request do
79
+ request.address(*arguments)
80
+ end
81
+ end
82
+ end
83
+
84
+ end
@@ -0,0 +1,20 @@
1
+ # Defines HTTY::CLI::Commands::BodyClear.
2
+
3
+ require File.expand_path("#{File.dirname __FILE__}/../command")
4
+ require File.expand_path("#{File.dirname __FILE__}/body_unset")
5
+
6
+ module HTTY; end
7
+
8
+ class HTTY::CLI; end
9
+
10
+ module HTTY::CLI::Commands; end
11
+
12
+ # Encapsulates the _body-clear_ command.
13
+ class HTTY::CLI::Commands::BodyClear < HTTY::CLI::Command
14
+
15
+ # Returns the command that the _body-clear_ command is an alias for.
16
+ def self.alias_for
17
+ HTTY::CLI::Commands::BodyUnset
18
+ end
19
+
20
+ end
@@ -0,0 +1,51 @@
1
+ # Defines HTTY::CLI::Commands::BodyRequest.
2
+
3
+ require File.expand_path("#{File.dirname __FILE__}/../command")
4
+ require File.expand_path("#{File.dirname __FILE__}/body_response")
5
+ require File.expand_path("#{File.dirname __FILE__}/body_set")
6
+ require File.expand_path("#{File.dirname __FILE__}/body_unset")
7
+ require File.expand_path("#{File.dirname __FILE__}/headers_request")
8
+
9
+ module HTTY; end
10
+
11
+ class HTTY::CLI; end
12
+
13
+ module HTTY::CLI::Commands; end
14
+
15
+ # Encapsulates the _body-request_ command.
16
+ class HTTY::CLI::Commands::BodyRequest < HTTY::CLI::Command
17
+
18
+ # Returns the name of a category under which help for the _body-request_
19
+ # command should appear.
20
+ def self.category
21
+ 'Building Requests'
22
+ end
23
+
24
+ # Returns the help text for the _body-request_ command.
25
+ def self.help
26
+ 'Displays the body of the request'
27
+ end
28
+
29
+ # Returns the extended help text for the _body-request_ command.
30
+ def self.help_extended
31
+ 'Displays the body content used for the request. Does not communicate ' +
32
+ 'with the endpoint.'
33
+ end
34
+
35
+ # Returns related command classes for the _body-request_ command.
36
+ def self.see_also_commands
37
+ [HTTY::CLI::Commands::BodySet,
38
+ HTTY::CLI::Commands::BodyUnset,
39
+ HTTY::CLI::Commands::HeadersRequest,
40
+ HTTY::CLI::Commands::BodyResponse]
41
+ end
42
+
43
+ # Performs the _body-request_ command.
44
+ def perform
45
+ if (body = session.requests.last.body)
46
+ puts body unless body.empty?
47
+ end
48
+ self
49
+ end
50
+
51
+ end
@@ -0,0 +1,59 @@
1
+ # Defines HTTY::CLI::Commands::BodyResponse.
2
+
3
+ require File.expand_path("#{File.dirname __FILE__}/../../no_response_error")
4
+ require File.expand_path("#{File.dirname __FILE__}/../command")
5
+ require File.expand_path("#{File.dirname __FILE__}/body_request")
6
+ require File.expand_path("#{File.dirname __FILE__}/headers_response")
7
+ require File.expand_path("#{File.dirname __FILE__}/status")
8
+
9
+ module HTTY; end
10
+
11
+ class HTTY::CLI; end
12
+
13
+ module HTTY::CLI::Commands; end
14
+
15
+ # Encapsulates the _body-response_ command.
16
+ class HTTY::CLI::Commands::BodyResponse < HTTY::CLI::Command
17
+
18
+ # Returns the name of a category under which help for the _body_ command
19
+ # should appear.
20
+ def self.category
21
+ 'Inspecting Responses'
22
+ end
23
+
24
+ # Returns the string used to invoke the _body-response_ command from the
25
+ # command line.
26
+ def self.command_line
27
+ 'body[-response]'
28
+ end
29
+
30
+ # Returns the help text for the _body-response_ command.
31
+ def self.help
32
+ 'Displays the body of the response'
33
+ end
34
+
35
+ # Returns the extended help text for the _body-response_ command.
36
+ def self.help_extended
37
+ 'Displays the body content received in the response. Does not ' +
38
+ 'communicate with the endpoint.'
39
+ end
40
+
41
+ # Returns related command classes for the _body-response_ command.
42
+ def self.see_also_commands
43
+ [HTTY::CLI::Commands::HeadersResponse,
44
+ HTTY::CLI::Commands::Status,
45
+ HTTY::CLI::Commands::BodyRequest]
46
+ end
47
+
48
+ # Performs the _body-response_ command.
49
+ def perform
50
+ unless (response = session.last_response)
51
+ raise HTTY::NoResponseError
52
+ end
53
+ unless (body = response.body).strip.empty?
54
+ puts body
55
+ end
56
+ self
57
+ end
58
+
59
+ end
@@ -0,0 +1,58 @@
1
+ # Defines HTTY::CLI::Commands::BodySet.
2
+
3
+ require File.expand_path("#{File.dirname __FILE__}/../command")
4
+ require File.expand_path("#{File.dirname __FILE__}/body_request")
5
+ require File.expand_path("#{File.dirname __FILE__}/body_unset")
6
+
7
+ module HTTY; end
8
+
9
+ class HTTY::CLI; end
10
+
11
+ module HTTY::CLI::Commands; end
12
+
13
+ # Encapsulates the _body-set_ command.
14
+ class HTTY::CLI::Commands::BodySet < HTTY::CLI::Command
15
+
16
+ # Returns the name of a category under which help for the _body-set_ command
17
+ # should appear.
18
+ def self.category
19
+ 'Building Requests'
20
+ end
21
+
22
+ # Returns the help text for the _body-set_ command.
23
+ def self.help
24
+ 'Sets the body of the request'
25
+ end
26
+
27
+ # Returns the extended help text for the _body-set_ command.
28
+ def self.help_extended
29
+ 'Sets the body content used for the request. Does not communicate with ' +
30
+ "the endpoint.\n" +
31
+ "\n" +
32
+ 'Hit Return three times in a row to signify the end of the body.'
33
+ end
34
+
35
+ # Returns related command classes for the _body-set_ command.
36
+ def self.see_also_commands
37
+ [HTTY::CLI::Commands::BodyRequest, HTTY::CLI::Commands::BodyUnset]
38
+ end
39
+
40
+ # Performs the _body-set_ command.
41
+ def perform
42
+ add_request_if_has_response do |request|
43
+ lines = []
44
+ empty_line_count = 0
45
+ while empty_line_count < 2 do
46
+ lines << $stdin.gets.chomp
47
+ empty_line_count = lines.last.empty? ? (empty_line_count + 1) : 0
48
+ end
49
+ request.body_set lines.join("\n").gsub(/[\r\n]+$/, '')
50
+ body = lines.join("\n")
51
+ while body.chomp != body
52
+ body.chomp!
53
+ end
54
+ request.body_set body
55
+ end
56
+ end
57
+
58
+ end
@@ -0,0 +1,45 @@
1
+ # Defines HTTY::CLI::Commands::BodyUnset.
2
+
3
+ require File.expand_path("#{File.dirname __FILE__}/../command")
4
+ require File.expand_path("#{File.dirname __FILE__}/body_request")
5
+ require File.expand_path("#{File.dirname __FILE__}/body_set")
6
+
7
+ module HTTY; end
8
+
9
+ class HTTY::CLI; end
10
+
11
+ module HTTY::CLI::Commands; end
12
+
13
+ # Encapsulates the _body-unset_ command.
14
+ class HTTY::CLI::Commands::BodyUnset < HTTY::CLI::Command
15
+
16
+ # Returns the name of a category under which help for the _body-unset_ command
17
+ # should appear.
18
+ def self.category
19
+ 'Building Requests'
20
+ end
21
+
22
+ # Returns the help text for the _body-unset_ command.
23
+ def self.help
24
+ 'Clears the body of the request'
25
+ end
26
+
27
+ # Returns the extended help text for the _body-unset_ command.
28
+ def self.help_extended
29
+ 'Clears the body content used for the request. Does not communicate with ' +
30
+ 'the endpoint.'
31
+ end
32
+
33
+ # Returns related command classes for the _body-unset_ command.
34
+ def self.see_also_commands
35
+ [HTTY::CLI::Commands::BodyRequest, HTTY::CLI::Commands::BodySet]
36
+ end
37
+
38
+ # Performs the _body-unset_ command.
39
+ def perform
40
+ add_request_if_has_response do |request|
41
+ request.body_unset
42
+ end
43
+ end
44
+
45
+ end
@@ -0,0 +1,20 @@
1
+ # Defines HTTY::CLI::Commands::Cd.
2
+
3
+ require File.expand_path("#{File.dirname __FILE__}/../command")
4
+ require File.expand_path("#{File.dirname __FILE__}/path_set")
5
+
6
+ module HTTY; end
7
+
8
+ class HTTY::CLI; end
9
+
10
+ module HTTY::CLI::Commands; end
11
+
12
+ # Encapsulates the _cd_ command.
13
+ class HTTY::CLI::Commands::Cd < HTTY::CLI::Command
14
+
15
+ # Returns the command that the _cd_ command is an alias for.
16
+ def self.alias_for
17
+ HTTY::CLI::Commands::PathSet
18
+ end
19
+
20
+ end
@@ -0,0 +1,20 @@
1
+ # Defines HTTY::CLI::Commands::CookieAdd.
2
+
3
+ require File.expand_path("#{File.dirname __FILE__}/../command")
4
+ require File.expand_path("#{File.dirname __FILE__}/cookies_add")
5
+
6
+ module HTTY; end
7
+
8
+ class HTTY::CLI; end
9
+
10
+ module HTTY::CLI::Commands; end
11
+
12
+ # Encapsulates the _cookie-add_ command.
13
+ class HTTY::CLI::Commands::CookieAdd < HTTY::CLI::Command
14
+
15
+ # Returns the command that the _cookie-add_ command is an alias for.
16
+ def self.alias_for
17
+ HTTY::CLI::Commands::CookiesAdd
18
+ end
19
+
20
+ end
@@ -0,0 +1,20 @@
1
+ # Defines HTTY::CLI::Commands::CookieRemove.
2
+
3
+ require File.expand_path("#{File.dirname __FILE__}/../command")
4
+ require File.expand_path("#{File.dirname __FILE__}/cookies_remove")
5
+
6
+ module HTTY; end
7
+
8
+ class HTTY::CLI; end
9
+
10
+ module HTTY::CLI::Commands; end
11
+
12
+ # Encapsulates the _cookie-remove_ command.
13
+ class HTTY::CLI::Commands::CookieRemove < HTTY::CLI::Command
14
+
15
+ # Returns the command that the _cookie-remove_ command is an alias for.
16
+ def self.alias_for
17
+ HTTY::CLI::Commands::CookiesRemove
18
+ end
19
+
20
+ end
@@ -0,0 +1,68 @@
1
+ # Defines HTTY::CLI::Commands::Cookies.
2
+
3
+ require File.expand_path("#{File.dirname __FILE__}/../command")
4
+ require File.expand_path("#{File.dirname __FILE__}/cookies_add")
5
+ require File.expand_path("#{File.dirname __FILE__}/cookies_remove")
6
+ require File.expand_path("#{File.dirname __FILE__}/cookies_remove_all")
7
+ require File.expand_path("#{File.dirname __FILE__}/cookies_use")
8
+ require File.expand_path("#{File.dirname __FILE__}/headers_request")
9
+ require File.expand_path("#{File.dirname __FILE__}/headers_response")
10
+
11
+ module HTTY; end
12
+
13
+ class HTTY::CLI; end
14
+
15
+ module HTTY::CLI::Commands; end
16
+
17
+ # Encapsulates the _cookies_ command.
18
+ class HTTY::CLI::Commands::Cookies < HTTY::CLI::Command
19
+
20
+ # Returns the name of a category under which help for the _cookies_ command
21
+ # should appear.
22
+ def self.category
23
+ 'Building Requests'
24
+ end
25
+
26
+ # Returns the help text for the _cookies_ command.
27
+ def self.help
28
+ 'Displays the cookies of the request'
29
+ end
30
+
31
+ # Returns the extended help text for the _cookies_ command.
32
+ def self.help_extended
33
+ 'Displays the cookies used for the request. Does not communicate with ' +
34
+ "the endpoint.\n" +
35
+ "\n" +
36
+ "This command displays cookies extracted from the request's 'Cookie' " +
37
+ "header, which is nevertheless shown when you type 'headers-request'.\n" +
38
+ "\n" +
39
+ 'Cookies are not required to have unique names. You can add multiple ' +
40
+ 'cookies with the same name, and they will be removed in ' +
41
+ "last-in-first-out order.\n" +
42
+ "\n" +
43
+ 'Cookies are cleared automatically when you change hosts.'
44
+ end
45
+
46
+ # Returns related command classes for the _cookies_ command.
47
+ def self.see_also_commands
48
+ [HTTY::CLI::Commands::CookiesAdd,
49
+ HTTY::CLI::Commands::CookiesRemove,
50
+ HTTY::CLI::Commands::CookiesRemoveAll,
51
+ HTTY::CLI::Commands::CookiesUse,
52
+ HTTY::CLI::Commands::HeadersRequest,
53
+ HTTY::CLI::Commands::HeadersResponse]
54
+ end
55
+
56
+ # Performs the _cookies_ command.
57
+ def perform
58
+ cookies = session.requests.last.cookies
59
+ margin = cookies.inject 0 do |result, cookie|
60
+ [cookie.first.length, result].max
61
+ end
62
+ cookies.each do |name, value|
63
+ puts "#{name.rjust margin}: #{value}"
64
+ end
65
+ self
66
+ end
67
+
68
+ end