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 |  |