em-http-request 0.2.10 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of em-http-request might be problematic. Click here for more details.
- data/LICENSE +58 -58
- data/README.md +157 -0
- data/Rakefile +110 -106
- data/VERSION +1 -1
- data/em-http-request.gemspec +5 -4
- data/examples/fetch.rb +30 -30
- data/examples/fibered-http.rb +38 -38
- data/examples/oauth-tweet.rb +49 -49
- data/examples/websocket-handler.rb +28 -28
- data/examples/websocket-server.rb +8 -8
- data/ext/buffer/extconf.rb +53 -53
- data/ext/http11_client/ext_help.h +14 -14
- data/ext/http11_client/extconf.rb +6 -6
- data/ext/http11_client/http11_client.c +328 -328
- data/ext/http11_client/http11_parser.c +418 -418
- data/ext/http11_client/http11_parser.h +48 -48
- data/ext/http11_client/http11_parser.rl +170 -170
- data/lib/em-http-request.rb +1 -1
- data/lib/em-http.rb +2 -1
- data/lib/em-http/client.rb +58 -26
- data/lib/em-http/core_ext/bytesize.rb +5 -5
- data/lib/em-http/core_ext/hash.rb +53 -53
- data/lib/em-http/decoders.rb +122 -122
- data/lib/em-http/http_options.rb +33 -31
- data/lib/em-http/mock.rb +90 -50
- data/lib/em-http/multi.rb +21 -17
- data/lib/em-http/request.rb +1 -0
- data/spec/fixtures/google.ca +20 -21
- data/spec/hash_spec.rb +24 -24
- data/spec/helper.rb +3 -2
- data/spec/mock_spec.rb +79 -34
- data/spec/multi_spec.rb +27 -10
- data/spec/request_spec.rb +198 -65
- data/spec/spec.opts +7 -0
- data/spec/stallion.rb +59 -2
- data/spec/stub_server.rb +22 -22
- metadata +6 -5
- data/README.rdoc +0 -138
@@ -1,48 +1,48 @@
|
|
1
|
-
/**
|
2
|
-
* Copyright (c) 2005 Zed A. Shaw
|
3
|
-
* You can redistribute it and/or modify it under the same terms as Ruby.
|
4
|
-
*/
|
5
|
-
|
6
|
-
#ifndef http11_parser_h
|
7
|
-
#define http11_parser_h
|
8
|
-
|
9
|
-
#include <sys/types.h>
|
10
|
-
|
11
|
-
#if defined(_WIN32)
|
12
|
-
#include <stddef.h>
|
13
|
-
#endif
|
14
|
-
|
15
|
-
typedef void (*element_cb)(void *data, const char *at, size_t length);
|
16
|
-
typedef void (*field_cb)(void *data, const char *field, size_t flen, const char *value, size_t vlen);
|
17
|
-
|
18
|
-
typedef struct httpclient_parser {
|
19
|
-
int cs;
|
20
|
-
size_t body_start;
|
21
|
-
int content_len;
|
22
|
-
size_t nread;
|
23
|
-
size_t mark;
|
24
|
-
size_t field_start;
|
25
|
-
size_t field_len;
|
26
|
-
|
27
|
-
void *data;
|
28
|
-
|
29
|
-
field_cb http_field;
|
30
|
-
element_cb reason_phrase;
|
31
|
-
element_cb status_code;
|
32
|
-
element_cb chunk_size;
|
33
|
-
element_cb http_version;
|
34
|
-
element_cb header_done;
|
35
|
-
element_cb last_chunk;
|
36
|
-
|
37
|
-
|
38
|
-
} httpclient_parser;
|
39
|
-
|
40
|
-
int httpclient_parser_init(httpclient_parser *parser);
|
41
|
-
int httpclient_parser_finish(httpclient_parser *parser);
|
42
|
-
size_t httpclient_parser_execute(httpclient_parser *parser, const char *data, size_t len, size_t off);
|
43
|
-
int httpclient_parser_has_error(httpclient_parser *parser);
|
44
|
-
int httpclient_parser_is_finished(httpclient_parser *parser);
|
45
|
-
|
46
|
-
#define httpclient_parser_nread(parser) (parser)->nread
|
47
|
-
|
48
|
-
#endif
|
1
|
+
/**
|
2
|
+
* Copyright (c) 2005 Zed A. Shaw
|
3
|
+
* You can redistribute it and/or modify it under the same terms as Ruby.
|
4
|
+
*/
|
5
|
+
|
6
|
+
#ifndef http11_parser_h
|
7
|
+
#define http11_parser_h
|
8
|
+
|
9
|
+
#include <sys/types.h>
|
10
|
+
|
11
|
+
#if defined(_WIN32)
|
12
|
+
#include <stddef.h>
|
13
|
+
#endif
|
14
|
+
|
15
|
+
typedef void (*element_cb)(void *data, const char *at, size_t length);
|
16
|
+
typedef void (*field_cb)(void *data, const char *field, size_t flen, const char *value, size_t vlen);
|
17
|
+
|
18
|
+
typedef struct httpclient_parser {
|
19
|
+
int cs;
|
20
|
+
size_t body_start;
|
21
|
+
int content_len;
|
22
|
+
size_t nread;
|
23
|
+
size_t mark;
|
24
|
+
size_t field_start;
|
25
|
+
size_t field_len;
|
26
|
+
|
27
|
+
void *data;
|
28
|
+
|
29
|
+
field_cb http_field;
|
30
|
+
element_cb reason_phrase;
|
31
|
+
element_cb status_code;
|
32
|
+
element_cb chunk_size;
|
33
|
+
element_cb http_version;
|
34
|
+
element_cb header_done;
|
35
|
+
element_cb last_chunk;
|
36
|
+
|
37
|
+
|
38
|
+
} httpclient_parser;
|
39
|
+
|
40
|
+
int httpclient_parser_init(httpclient_parser *parser);
|
41
|
+
int httpclient_parser_finish(httpclient_parser *parser);
|
42
|
+
size_t httpclient_parser_execute(httpclient_parser *parser, const char *data, size_t len, size_t off);
|
43
|
+
int httpclient_parser_has_error(httpclient_parser *parser);
|
44
|
+
int httpclient_parser_is_finished(httpclient_parser *parser);
|
45
|
+
|
46
|
+
#define httpclient_parser_nread(parser) (parser)->nread
|
47
|
+
|
48
|
+
#endif
|
@@ -1,170 +1,170 @@
|
|
1
|
-
/**
|
2
|
-
* Copyright (c) 2005 Zed A. Shaw
|
3
|
-
* You can redistribute it and/or modify it under the same terms as Ruby.
|
4
|
-
*/
|
5
|
-
|
6
|
-
#include "http11_parser.h"
|
7
|
-
#include <stdio.h>
|
8
|
-
#include <assert.h>
|
9
|
-
#include <stdlib.h>
|
10
|
-
#include <ctype.h>
|
11
|
-
#include <string.h>
|
12
|
-
|
13
|
-
#define LEN(AT, FPC) (FPC - buffer - parser->AT)
|
14
|
-
#define MARK(M,FPC) (parser->M = (FPC) - buffer)
|
15
|
-
#define PTR_TO(F) (buffer + parser->F)
|
16
|
-
#define L(M) fprintf(stderr, "" # M "\n");
|
17
|
-
|
18
|
-
|
19
|
-
/** machine **/
|
20
|
-
%%{
|
21
|
-
machine httpclient_parser;
|
22
|
-
|
23
|
-
action mark {MARK(mark, fpc); }
|
24
|
-
|
25
|
-
action start_field { MARK(field_start, fpc); }
|
26
|
-
|
27
|
-
action write_field {
|
28
|
-
parser->field_len = LEN(field_start, fpc);
|
29
|
-
}
|
30
|
-
|
31
|
-
action start_value { MARK(mark, fpc); }
|
32
|
-
|
33
|
-
action write_value {
|
34
|
-
parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc));
|
35
|
-
}
|
36
|
-
|
37
|
-
action reason_phrase {
|
38
|
-
parser->reason_phrase(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
39
|
-
}
|
40
|
-
|
41
|
-
action status_code {
|
42
|
-
parser->status_code(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
43
|
-
}
|
44
|
-
|
45
|
-
action http_version {
|
46
|
-
parser->http_version(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
47
|
-
}
|
48
|
-
|
49
|
-
action chunk_size {
|
50
|
-
parser->chunk_size(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
51
|
-
}
|
52
|
-
|
53
|
-
action last_chunk {
|
54
|
-
parser->last_chunk(parser->data, NULL, 0);
|
55
|
-
}
|
56
|
-
|
57
|
-
action done {
|
58
|
-
parser->body_start = fpc - buffer + 1;
|
59
|
-
if(parser->header_done != NULL)
|
60
|
-
parser->header_done(parser->data, fpc + 1, pe - fpc - 1);
|
61
|
-
fbreak;
|
62
|
-
}
|
63
|
-
|
64
|
-
# line endings
|
65
|
-
CRLF = ("\r\n" | "\n");
|
66
|
-
|
67
|
-
# character types
|
68
|
-
CTL = (cntrl | 127);
|
69
|
-
tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");
|
70
|
-
|
71
|
-
# elements
|
72
|
-
token = (ascii -- (CTL | tspecials));
|
73
|
-
|
74
|
-
Reason_Phrase = (any -- CRLF)* >mark %reason_phrase;
|
75
|
-
Status_Code = digit{3} >mark %status_code;
|
76
|
-
http_number = (digit+ "." digit+) ;
|
77
|
-
HTTP_Version = ("HTTP/" http_number) >mark %http_version ;
|
78
|
-
Status_Line = HTTP_Version " " Status_Code " "? Reason_Phrase :> CRLF;
|
79
|
-
|
80
|
-
field_name = token+ >start_field %write_field;
|
81
|
-
field_value = any* >start_value %write_value;
|
82
|
-
message_header = field_name ":" " "* field_value :> CRLF;
|
83
|
-
|
84
|
-
Response = Status_Line (message_header)* (CRLF @done);
|
85
|
-
|
86
|
-
chunk_ext_val = token+;
|
87
|
-
chunk_ext_name = token+;
|
88
|
-
chunk_extension = (";" chunk_ext_name >start_field %write_field %start_value ("=" chunk_ext_val >start_value)? %write_value )*;
|
89
|
-
last_chunk = "0"? chunk_extension :> (CRLF @last_chunk @done);
|
90
|
-
chunk_size = xdigit+;
|
91
|
-
chunk = chunk_size >mark %chunk_size chunk_extension space* :> (CRLF @done);
|
92
|
-
Chunked_Header = (chunk | last_chunk);
|
93
|
-
|
94
|
-
main := Response | Chunked_Header;
|
95
|
-
}%%
|
96
|
-
|
97
|
-
/** Data **/
|
98
|
-
%% write data;
|
99
|
-
|
100
|
-
int httpclient_parser_init(httpclient_parser *parser) {
|
101
|
-
int cs = 0;
|
102
|
-
%% write init;
|
103
|
-
parser->cs = cs;
|
104
|
-
parser->body_start = 0;
|
105
|
-
parser->content_len = 0;
|
106
|
-
parser->mark = 0;
|
107
|
-
parser->nread = 0;
|
108
|
-
parser->field_len = 0;
|
109
|
-
parser->field_start = 0;
|
110
|
-
|
111
|
-
return(1);
|
112
|
-
}
|
113
|
-
|
114
|
-
|
115
|
-
/** exec **/
|
116
|
-
size_t httpclient_parser_execute(httpclient_parser *parser, const char *buffer, size_t len, size_t off) {
|
117
|
-
const char *p, *pe;
|
118
|
-
int cs = parser->cs;
|
119
|
-
|
120
|
-
assert(off <= len && "offset past end of buffer");
|
121
|
-
|
122
|
-
p = buffer+off;
|
123
|
-
pe = buffer+len;
|
124
|
-
|
125
|
-
assert(*pe == '\0' && "pointer does not end on NUL");
|
126
|
-
assert(pe - p == len - off && "pointers aren't same distance");
|
127
|
-
|
128
|
-
|
129
|
-
%% write exec;
|
130
|
-
|
131
|
-
parser->cs = cs;
|
132
|
-
parser->nread += p - (buffer + off);
|
133
|
-
|
134
|
-
assert(p <= pe && "buffer overflow after parsing execute");
|
135
|
-
assert(parser->nread <= len && "nread longer than length");
|
136
|
-
assert(parser->body_start <= len && "body starts after buffer end");
|
137
|
-
assert(parser->mark < len && "mark is after buffer end");
|
138
|
-
assert(parser->field_len <= len && "field has length longer than whole buffer");
|
139
|
-
assert(parser->field_start < len && "field starts after buffer end");
|
140
|
-
|
141
|
-
if(parser->body_start) {
|
142
|
-
/* final \r\n combo encountered so stop right here */
|
143
|
-
parser->nread = parser->body_start;
|
144
|
-
}
|
145
|
-
|
146
|
-
return(parser->nread);
|
147
|
-
}
|
148
|
-
|
149
|
-
int httpclient_parser_finish(httpclient_parser *parser)
|
150
|
-
{
|
151
|
-
int cs = parser->cs;
|
152
|
-
|
153
|
-
parser->cs = cs;
|
154
|
-
|
155
|
-
if (httpclient_parser_has_error(parser) ) {
|
156
|
-
return -1;
|
157
|
-
} else if (httpclient_parser_is_finished(parser) ) {
|
158
|
-
return 1;
|
159
|
-
} else {
|
160
|
-
return 0;
|
161
|
-
}
|
162
|
-
}
|
163
|
-
|
164
|
-
int httpclient_parser_has_error(httpclient_parser *parser) {
|
165
|
-
return parser->cs == httpclient_parser_error;
|
166
|
-
}
|
167
|
-
|
168
|
-
int httpclient_parser_is_finished(httpclient_parser *parser) {
|
169
|
-
return parser->cs == httpclient_parser_first_final;
|
170
|
-
}
|
1
|
+
/**
|
2
|
+
* Copyright (c) 2005 Zed A. Shaw
|
3
|
+
* You can redistribute it and/or modify it under the same terms as Ruby.
|
4
|
+
*/
|
5
|
+
|
6
|
+
#include "http11_parser.h"
|
7
|
+
#include <stdio.h>
|
8
|
+
#include <assert.h>
|
9
|
+
#include <stdlib.h>
|
10
|
+
#include <ctype.h>
|
11
|
+
#include <string.h>
|
12
|
+
|
13
|
+
#define LEN(AT, FPC) (FPC - buffer - parser->AT)
|
14
|
+
#define MARK(M,FPC) (parser->M = (FPC) - buffer)
|
15
|
+
#define PTR_TO(F) (buffer + parser->F)
|
16
|
+
#define L(M) fprintf(stderr, "" # M "\n");
|
17
|
+
|
18
|
+
|
19
|
+
/** machine **/
|
20
|
+
%%{
|
21
|
+
machine httpclient_parser;
|
22
|
+
|
23
|
+
action mark {MARK(mark, fpc); }
|
24
|
+
|
25
|
+
action start_field { MARK(field_start, fpc); }
|
26
|
+
|
27
|
+
action write_field {
|
28
|
+
parser->field_len = LEN(field_start, fpc);
|
29
|
+
}
|
30
|
+
|
31
|
+
action start_value { MARK(mark, fpc); }
|
32
|
+
|
33
|
+
action write_value {
|
34
|
+
parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc));
|
35
|
+
}
|
36
|
+
|
37
|
+
action reason_phrase {
|
38
|
+
parser->reason_phrase(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
39
|
+
}
|
40
|
+
|
41
|
+
action status_code {
|
42
|
+
parser->status_code(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
43
|
+
}
|
44
|
+
|
45
|
+
action http_version {
|
46
|
+
parser->http_version(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
47
|
+
}
|
48
|
+
|
49
|
+
action chunk_size {
|
50
|
+
parser->chunk_size(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
51
|
+
}
|
52
|
+
|
53
|
+
action last_chunk {
|
54
|
+
parser->last_chunk(parser->data, NULL, 0);
|
55
|
+
}
|
56
|
+
|
57
|
+
action done {
|
58
|
+
parser->body_start = fpc - buffer + 1;
|
59
|
+
if(parser->header_done != NULL)
|
60
|
+
parser->header_done(parser->data, fpc + 1, pe - fpc - 1);
|
61
|
+
fbreak;
|
62
|
+
}
|
63
|
+
|
64
|
+
# line endings
|
65
|
+
CRLF = ("\r\n" | "\n");
|
66
|
+
|
67
|
+
# character types
|
68
|
+
CTL = (cntrl | 127);
|
69
|
+
tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");
|
70
|
+
|
71
|
+
# elements
|
72
|
+
token = (ascii -- (CTL | tspecials));
|
73
|
+
|
74
|
+
Reason_Phrase = (any -- CRLF)* >mark %reason_phrase;
|
75
|
+
Status_Code = digit{3} >mark %status_code;
|
76
|
+
http_number = (digit+ "." digit+) ;
|
77
|
+
HTTP_Version = ("HTTP/" http_number) >mark %http_version ;
|
78
|
+
Status_Line = HTTP_Version " " Status_Code " "? Reason_Phrase :> CRLF;
|
79
|
+
|
80
|
+
field_name = token+ >start_field %write_field;
|
81
|
+
field_value = any* >start_value %write_value;
|
82
|
+
message_header = field_name ":" " "* field_value :> CRLF;
|
83
|
+
|
84
|
+
Response = Status_Line (message_header)* (CRLF @done);
|
85
|
+
|
86
|
+
chunk_ext_val = token+;
|
87
|
+
chunk_ext_name = token+;
|
88
|
+
chunk_extension = (";" chunk_ext_name >start_field %write_field %start_value ("=" chunk_ext_val >start_value)? %write_value )*;
|
89
|
+
last_chunk = "0"? chunk_extension :> (CRLF @last_chunk @done);
|
90
|
+
chunk_size = xdigit+;
|
91
|
+
chunk = chunk_size >mark %chunk_size chunk_extension space* :> (CRLF @done);
|
92
|
+
Chunked_Header = (chunk | last_chunk);
|
93
|
+
|
94
|
+
main := Response | Chunked_Header;
|
95
|
+
}%%
|
96
|
+
|
97
|
+
/** Data **/
|
98
|
+
%% write data;
|
99
|
+
|
100
|
+
int httpclient_parser_init(httpclient_parser *parser) {
|
101
|
+
int cs = 0;
|
102
|
+
%% write init;
|
103
|
+
parser->cs = cs;
|
104
|
+
parser->body_start = 0;
|
105
|
+
parser->content_len = 0;
|
106
|
+
parser->mark = 0;
|
107
|
+
parser->nread = 0;
|
108
|
+
parser->field_len = 0;
|
109
|
+
parser->field_start = 0;
|
110
|
+
|
111
|
+
return(1);
|
112
|
+
}
|
113
|
+
|
114
|
+
|
115
|
+
/** exec **/
|
116
|
+
size_t httpclient_parser_execute(httpclient_parser *parser, const char *buffer, size_t len, size_t off) {
|
117
|
+
const char *p, *pe;
|
118
|
+
int cs = parser->cs;
|
119
|
+
|
120
|
+
assert(off <= len && "offset past end of buffer");
|
121
|
+
|
122
|
+
p = buffer+off;
|
123
|
+
pe = buffer+len;
|
124
|
+
|
125
|
+
assert(*pe == '\0' && "pointer does not end on NUL");
|
126
|
+
assert(pe - p == len - off && "pointers aren't same distance");
|
127
|
+
|
128
|
+
|
129
|
+
%% write exec;
|
130
|
+
|
131
|
+
parser->cs = cs;
|
132
|
+
parser->nread += p - (buffer + off);
|
133
|
+
|
134
|
+
assert(p <= pe && "buffer overflow after parsing execute");
|
135
|
+
assert(parser->nread <= len && "nread longer than length");
|
136
|
+
assert(parser->body_start <= len && "body starts after buffer end");
|
137
|
+
assert(parser->mark < len && "mark is after buffer end");
|
138
|
+
assert(parser->field_len <= len && "field has length longer than whole buffer");
|
139
|
+
assert(parser->field_start < len && "field starts after buffer end");
|
140
|
+
|
141
|
+
if(parser->body_start) {
|
142
|
+
/* final \r\n combo encountered so stop right here */
|
143
|
+
parser->nread = parser->body_start;
|
144
|
+
}
|
145
|
+
|
146
|
+
return(parser->nread);
|
147
|
+
}
|
148
|
+
|
149
|
+
int httpclient_parser_finish(httpclient_parser *parser)
|
150
|
+
{
|
151
|
+
int cs = parser->cs;
|
152
|
+
|
153
|
+
parser->cs = cs;
|
154
|
+
|
155
|
+
if (httpclient_parser_has_error(parser) ) {
|
156
|
+
return -1;
|
157
|
+
} else if (httpclient_parser_is_finished(parser) ) {
|
158
|
+
return 1;
|
159
|
+
} else {
|
160
|
+
return 0;
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
int httpclient_parser_has_error(httpclient_parser *parser) {
|
165
|
+
return parser->cs == httpclient_parser_error;
|
166
|
+
}
|
167
|
+
|
168
|
+
int httpclient_parser_is_finished(httpclient_parser *parser) {
|
169
|
+
return parser->cs == httpclient_parser_first_final;
|
170
|
+
}
|
data/lib/em-http-request.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require 'em-http'
|
1
|
+
require 'em-http'
|
data/lib/em-http.rb
CHANGED
@@ -16,4 +16,5 @@ require File.dirname(__FILE__) + '/em-http/client'
|
|
16
16
|
require File.dirname(__FILE__) + '/em-http/multi'
|
17
17
|
require File.dirname(__FILE__) + '/em-http/request'
|
18
18
|
require File.dirname(__FILE__) + '/em-http/decoders'
|
19
|
-
require File.dirname(__FILE__) + '/em-http/http_options'
|
19
|
+
require File.dirname(__FILE__) + '/em-http/http_options'
|
20
|
+
require File.dirname(__FILE__) + '/em-http/mock'
|
data/lib/em-http/client.rb
CHANGED
@@ -23,12 +23,11 @@ module EventMachine
|
|
23
23
|
|
24
24
|
# E-Tag
|
25
25
|
def etag
|
26
|
-
self[
|
26
|
+
self[HttpClient::ETAG]
|
27
27
|
end
|
28
28
|
|
29
29
|
def last_modified
|
30
|
-
|
31
|
-
Time.parse(time) if time
|
30
|
+
self[HttpClient::LAST_MODIFIED]
|
32
31
|
end
|
33
32
|
|
34
33
|
# HTTP response status as an integer
|
@@ -84,7 +83,7 @@ module EventMachine
|
|
84
83
|
# Escapes a URI.
|
85
84
|
def escape(s)
|
86
85
|
s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
|
87
|
-
'%'+$1.unpack('H2'
|
86
|
+
'%'+$1.unpack('H2'*bytesize($1)).join('%').upcase
|
88
87
|
}.tr(' ', '+')
|
89
88
|
end
|
90
89
|
|
@@ -95,6 +94,16 @@ module EventMachine
|
|
95
94
|
}
|
96
95
|
end
|
97
96
|
|
97
|
+
if ''.respond_to?(:bytesize)
|
98
|
+
def bytesize(string)
|
99
|
+
string.bytesize
|
100
|
+
end
|
101
|
+
else
|
102
|
+
def bytesize(string)
|
103
|
+
string.size
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
98
107
|
# Map all header keys to a downcased string version
|
99
108
|
def munge_header_keys(head)
|
100
109
|
head.inject({}) { |h, (k, v)| h[k.to_s.downcase] = v; h }
|
@@ -111,21 +120,27 @@ module EventMachine
|
|
111
120
|
end
|
112
121
|
end
|
113
122
|
|
114
|
-
def encode_request(method,
|
115
|
-
|
123
|
+
def encode_request(method, uri, query, proxy)
|
124
|
+
query = encode_query(uri, query)
|
125
|
+
|
126
|
+
# Non CONNECT proxies require that you provide the full request
|
127
|
+
# uri in request header, as opposed to a relative path.
|
128
|
+
query = uri.join(query) if proxy and not proxy[:use_connect]
|
129
|
+
|
130
|
+
HTTP_REQUEST_HEADER % [method.to_s.upcase, query]
|
116
131
|
end
|
117
132
|
|
118
|
-
def encode_query(
|
133
|
+
def encode_query(uri, query)
|
119
134
|
encoded_query = if query.kind_of?(Hash)
|
120
135
|
query.map { |k, v| encode_param(k, v) }.join('&')
|
121
136
|
else
|
122
137
|
query.to_s
|
123
138
|
end
|
124
|
-
|
125
|
-
|
139
|
+
|
140
|
+
if !uri.query.to_s.empty?
|
141
|
+
encoded_query = [encoded_query, uri.query].reject {|part| part.empty?}.join("&")
|
126
142
|
end
|
127
|
-
|
128
|
-
"#{path}?#{encoded_query}"
|
143
|
+
encoded_query.to_s.empty? ? uri.path : "#{uri.path}?#{encoded_query}"
|
129
144
|
end
|
130
145
|
|
131
146
|
# URL encodes query parameters:
|
@@ -183,10 +198,13 @@ module EventMachine
|
|
183
198
|
TRANSFER_ENCODING="TRANSFER_ENCODING"
|
184
199
|
CONTENT_ENCODING="CONTENT_ENCODING"
|
185
200
|
CONTENT_LENGTH="CONTENT_LENGTH"
|
201
|
+
LAST_MODIFIED="LAST_MODIFIED"
|
186
202
|
KEEP_ALIVE="CONNECTION"
|
187
203
|
SET_COOKIE="SET_COOKIE"
|
188
204
|
LOCATION="LOCATION"
|
189
205
|
HOST="HOST"
|
206
|
+
ETAG="ETAG"
|
207
|
+
|
190
208
|
CRLF="\r\n"
|
191
209
|
|
192
210
|
attr_accessor :method, :options, :uri
|
@@ -210,10 +228,10 @@ module EventMachine
|
|
210
228
|
|
211
229
|
# start HTTP request once we establish connection to host
|
212
230
|
def connection_completed
|
213
|
-
# if
|
214
|
-
#
|
215
|
-
if
|
216
|
-
@state = :
|
231
|
+
# if we need to negotiate the proxy connection first, then
|
232
|
+
# issue a CONNECT query and wait for 200 response
|
233
|
+
if connect_proxy? and @state == :response_header
|
234
|
+
@state = :connect_proxy
|
217
235
|
send_request_header
|
218
236
|
|
219
237
|
# if connecting via proxy, then state will be :proxy_connected,
|
@@ -283,23 +301,32 @@ module EventMachine
|
|
283
301
|
end
|
284
302
|
|
285
303
|
def websocket?; @uri.scheme == 'ws'; end
|
304
|
+
def proxy?; !@options[:proxy].nil?; end
|
305
|
+
def connect_proxy?; proxy? && (@options[:proxy][:use_connect] == true); end
|
286
306
|
|
287
307
|
def send_request_header
|
288
308
|
query = @options[:query]
|
289
309
|
head = @options[:head] ? munge_header_keys(@options[:head]) : {}
|
290
310
|
file = @options[:file]
|
311
|
+
proxy = @options[:proxy]
|
291
312
|
body = normalize_body
|
292
|
-
request_header = nil
|
293
313
|
|
294
|
-
|
295
|
-
proxy = @options[:proxy]
|
314
|
+
request_header = nil
|
296
315
|
|
297
|
-
|
316
|
+
if proxy
|
317
|
+
# initialize headers for the http proxy
|
298
318
|
head = proxy[:head] ? munge_header_keys(proxy[:head]) : {}
|
299
319
|
head['proxy-authorization'] = proxy[:authorization] if proxy[:authorization]
|
300
|
-
request_header = HTTP_REQUEST_HEADER % ['CONNECT', "#{@uri.host}:#{@uri.port}"]
|
301
320
|
|
302
|
-
|
321
|
+
# if we need to negotiate the tunnel connection first, then
|
322
|
+
# issue a CONNECT query to the proxy first. This is an optional
|
323
|
+
# flag, by default we will provide full URIs to the proxy
|
324
|
+
if @state == :connect_proxy
|
325
|
+
request_header = HTTP_REQUEST_HEADER % ['CONNECT', "#{@uri.host}:#{@uri.port}"]
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
if websocket?
|
303
330
|
head['upgrade'] = 'WebSocket'
|
304
331
|
head['connection'] = 'Upgrade'
|
305
332
|
head['origin'] = @options[:origin] || @uri.host
|
@@ -332,7 +359,7 @@ module EventMachine
|
|
332
359
|
@last_effective_url = @uri
|
333
360
|
|
334
361
|
# Build the request headers
|
335
|
-
request_header ||= encode_request(@method, @uri
|
362
|
+
request_header ||= encode_request(@method, @uri, query, proxy)
|
336
363
|
request_header << encode_headers(head)
|
337
364
|
request_header << CRLF
|
338
365
|
send_data request_header
|
@@ -388,6 +415,7 @@ module EventMachine
|
|
388
415
|
|
389
416
|
@response_header = HttpResponseHeader.new
|
390
417
|
@state = :response_header
|
418
|
+
@response = ''
|
391
419
|
@data.clear
|
392
420
|
else
|
393
421
|
if @state == :finished || (@state == :body && @bytes_remaining.nil?)
|
@@ -405,7 +433,7 @@ module EventMachine
|
|
405
433
|
|
406
434
|
def dispatch
|
407
435
|
while case @state
|
408
|
-
when :
|
436
|
+
when :connect_proxy
|
409
437
|
parse_response_header
|
410
438
|
when :response_header
|
411
439
|
parse_response_header
|
@@ -457,7 +485,7 @@ module EventMachine
|
|
457
485
|
return false
|
458
486
|
end
|
459
487
|
|
460
|
-
if @state == :
|
488
|
+
if @state == :connect_proxy
|
461
489
|
# when a successfull tunnel is established, the proxy responds with a
|
462
490
|
# 200 response code. from here, the tunnel is transparent.
|
463
491
|
if @response_header.http_status.to_i == 200
|
@@ -475,9 +503,13 @@ module EventMachine
|
|
475
503
|
if @response_header.location
|
476
504
|
begin
|
477
505
|
location = Addressable::URI.parse(@response_header.location)
|
506
|
+
|
478
507
|
if location.relative?
|
479
508
|
location = @uri.join(location)
|
480
509
|
@response_header[LOCATION] = location.to_s
|
510
|
+
else
|
511
|
+
# if redirect is to an absolute url, check for correct URI structure
|
512
|
+
raise if location.host.nil?
|
481
513
|
end
|
482
514
|
|
483
515
|
# store last url on any sign of redirect
|
@@ -628,8 +660,8 @@ module EventMachine
|
|
628
660
|
# slice the message out of the buffer and pass in
|
629
661
|
# for processing, and buffer data otherwise
|
630
662
|
buffer = @data.read
|
631
|
-
while msg = buffer.slice!(/\000([^\377]*)\377/)
|
632
|
-
msg.gsub!(
|
663
|
+
while msg = buffer.slice!(/\000([^\377]*)\377/n)
|
664
|
+
msg.gsub!(/\A\x00|\xff\z/n, '')
|
633
665
|
@stream.call(msg)
|
634
666
|
end
|
635
667
|
|