http.h (27348B)
1 /* 2 ------------------------------------------------------------------------------ 3 Licensing information can be found at the end of the file. 4 ------------------------------------------------------------------------------ 5 6 http.hpp - v1.0 - Basic HTTP protocol implementation over sockets (no https). 7 8 Do this: 9 #define HTTP_IMPLEMENTATION 10 before you include this file in *one* C/C++ file to create the implementation. 11 */ 12 13 #ifndef http_hpp 14 #define http_hpp 15 16 #define _CRT_NONSTDC_NO_DEPRECATE 17 #define _CRT_SECURE_NO_WARNINGS 18 #include <stddef.h> // for size_t 19 #include <stdint.h> // for uintptr_t 20 #include <sys/select.h> 21 22 typedef enum http_status_t 23 { 24 HTTP_STATUS_PENDING, 25 HTTP_STATUS_COMPLETED, 26 HTTP_STATUS_FAILED, 27 } http_status_t; 28 29 typedef struct http_t 30 { 31 http_status_t status; 32 int status_code; 33 char const* reason_phrase; 34 char const* content_type; 35 size_t response_size; 36 void* response_data; 37 } http_t; 38 39 http_t* http_get( char const* url, void* memctx ); 40 http_t* http_post( char const* url, void const* data, size_t size, void* memctx ); 41 42 http_status_t http_process( http_t* http ); 43 44 void http_release( http_t* http ); 45 46 #endif /* http_hpp */ 47 48 /** 49 50 http.hpp 51 ======== 52 53 Basic HTTP protocol implementation over sockets (no https). 54 55 56 Example 57 ------- 58 59 #define HTTP_IMPLEMENTATION 60 #include "http.h" 61 62 int main( int argc, char** argv ) { 63 http_t* request = http_get( "http://www.mattiasgustavsson.com/http_test.txt", NULL ); 64 if( !request ) { 65 printf( "Invalid request.\n" ); 66 return 1; 67 } 68 69 http_status_t status = HTTP_STATUS_PENDING; 70 int prev_size = -1; 71 while( status == HTTP_STATUS_PENDING ) { 72 status = http_process( request ); 73 if( prev_size != (int) request->response_size ) { 74 printf( "%d byte(s) received.\n", (int) request->response_size ); 75 prev_size = (int) request->response_size; 76 } 77 } 78 79 if( status == HTTP_STATUS_FAILED ) { 80 printf( "HTTP request failed (%d): %s.\n", request->status_code, request->reason_phrase ); 81 http_release( request ); 82 return 1; 83 } 84 85 printf( "\nContent type: %s\n\n%s\n", request->content_type, (char const*)request->response_data ); 86 http_release( request ); 87 return 0; 88 } 89 90 91 API Documentation 92 ----------------- 93 94 http.h is a small library for making http requests from a web server. It only supports GET and POST http commands, and 95 is designed for when you just need a very basic way of communicating over http. http.h does not support https 96 connections, just plain http. 97 98 http.h is a single-header library, and does not need any .lib files or other binaries, or any build scripts. To use 99 it, you just include http.h to get the API declarations. To get the definitions, you must include http.h from 100 *one* single C or C++ file, and #define the symbol `HTTP_IMPLEMENTATION` before you do. 101 102 103 #### Custom memory allocators 104 105 For working memory and to store the retrieved data, http.h needs to do dynamic allocation by calling `malloc`. Programs 106 might want to keep track of allocations done, or use custom defined pools to allocate memory from. http.h allows 107 for specifying custom memory allocation functions for `malloc` and `free`. This is done with the following code: 108 109 #define HTTP_IMPLEMENTATION 110 #define HTTP_MALLOC( ctx, size ) ( my_custom_malloc( ctx, size ) ) 111 #define HTTP_FREE( ctx, ptr ) ( my_custom_free( ctx, ptr ) ) 112 #include "http.h" 113 114 where `my_custom_malloc` and `my_custom_free` are your own memory allocation/deallocation functions. The `ctx` parameter 115 is an optional parameter of type `void*`. When `http_get` or `http_post` is called, , you can pass in a `memctx` 116 parameter, which can be a pointer to anything you like, and which will be passed through as the `ctx` parameter to every 117 `HTTP_MALLOC`/`HTTP_FREE` call. For example, if you are doing memory tracking, you can pass a pointer to your 118 tracking data as `memctx`, and in your custom allocation/deallocation function, you can cast the `ctx` param back to the 119 right type, and access the tracking data. 120 121 If no custom allocator is defined, http.h will default to `malloc` and `free` from the C runtime library. 122 123 124 http_get 125 -------- 126 127 http_t* http_get( char const* url, void* memctx ) 128 129 Initiates a http GET request with the specified url. `url` is a zero terminated string containing the request location, 130 just like you would type it in a browser, for example `http://www.mattiasgustavsson.com:80/http_test.txt`. `memctx` is a 131 pointer to user defined data which will be passed through to the custom HTTP_MALLOC/HTTP_FREE calls. It can be NULL if 132 no user defined data is needed. Returns a `http_t` instance, which needs to be passed to `http_process` to process the 133 request. When the request is finished (or have failed), the returned `http_t` instance needs to be released by calling 134 `http_release`. If the request was invalid, `http_get` returns NULL. 135 136 137 http_post 138 --------- 139 140 http_t* http_post( char const* url, void const* data, size_t size, void* memctx ) 141 142 Initiates a http POST request with the specified url. `url` is a zero terminated string containing the request location, 143 just like you would type it in a browser, for example `http://www.mattiasgustavsson.com:80/http_test.txt`. `data` is a 144 pointer to the data to be sent along as part of the request, and `size` is the number of bytes to send. `memctx` is a 145 pointer to user defined data which will be passed through to the custom HTTP_MALLOC/HTTP_FREE calls. It can be NULL if 146 no user defined data is needed. Returns a `http_t` instance, which needs to be passed to `http_process` to process the 147 request. When the request is finished (or have failed), the returned `http_t` instance needs to be released by calling 148 `http_release`. If the request was invalid, `http_post` returns NULL. 149 150 151 http_process 152 ------------ 153 154 http_status_t http_process( http_t* http ) 155 156 http.h uses non-blocking sockets, so after a request have been made by calling either `http_get` or `http_post`, you 157 have to keep calling `http_process` for as long as it returns `HTTP_STATUS_PENDING`. You can call it from a loop which 158 does other work too, for example from inside a game loop or from a loop which calls `http_process` on multiple requests. 159 If the request fails, `http_process` returns `HTTP_STATUS_FAILED`, and the fields `status_code` and `reason_phrase` may 160 contain more details (for example, status code can be 404 if the requested resource was not found on the server). If the 161 request completes successfully, it returns `HTTP_STATUS_COMPLETED`. In this case, the `http_t` instance will contain 162 details about the result. `status_code` and `reason_phrase` contains the details about the result, as specified in the 163 HTTP protocol. `content_type` contains the MIME type for the returns resource, for example `text/html` for a normal web 164 page. `response_data` is the pointer to the received data, and `resonse_size` is the number of bytes it contains. In the 165 case when the response data is in text format, http.h ensures there is a zero terminator placed immediately after the 166 response data block, so it is safe to interpret the resonse data as a `char*`. Note that the data size in this case will 167 be the length of the data without the additional zero terminator. 168 169 170 http_release 171 ------------ 172 173 void http_release( http_t* http ) 174 175 Releases the resources acquired by `http_get` or `http_post`. Should be call when you are finished with the request. 176 177 */ 178 179 /* 180 ---------------------- 181 IMPLEMENTATION 182 ---------------------- 183 */ 184 185 #ifdef HTTP_IMPLEMENTATION 186 187 #ifdef _WIN32 188 #define _CRT_NONSTDC_NO_DEPRECATE 189 #define _CRT_SECURE_NO_WARNINGS 190 #pragma warning( push ) 191 #pragma warning( disable: 4127 ) // conditional expression is constant 192 #pragma warning( disable: 4255 ) // 'function' : no function prototype given: converting '()' to '(void)' 193 #pragma warning( disable: 4365 ) // 'action' : conversion from 'type_1' to 'type_2', signed/unsigned mismatch 194 #pragma warning( disable: 4574 ) // 'Identifier' is defined to be '0': did you mean to use '#if identifier'? 195 #pragma warning( disable: 4668 ) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directive' 196 #pragma warning( disable: 4706 ) // assignment within conditional expression 197 #include <winsock2.h> 198 #include <ws2tcpip.h> 199 #pragma warning( pop ) 200 #pragma comment (lib, "Ws2_32.lib") 201 #include <string.h> 202 #include <stdio.h> 203 #define HTTP_SOCKET SOCKET 204 #define HTTP_INVALID_SOCKET INVALID_SOCKET 205 #else 206 #include <stdlib.h> 207 #include <stdio.h> 208 #include <string.h> 209 #include <sys/types.h> 210 #include <sys/socket.h> 211 #include <unistd.h> 212 #include <errno.h> 213 #include <fcntl.h> 214 #include <netdb.h> 215 #define HTTP_SOCKET int 216 #define HTTP_INVALID_SOCKET -1 217 #endif 218 219 #ifndef HTTP_MALLOC 220 #define _CRT_NONSTDC_NO_DEPRECATE 221 #define _CRT_SECURE_NO_WARNINGS 222 #include <stdlib.h> 223 #define HTTP_MALLOC( ctx, size ) ( malloc( size ) ) 224 #define HTTP_FREE( ctx, ptr ) ( free( ptr ) ) 225 #endif 226 227 typedef struct http_internal_t 228 { 229 /* keep this at the top!*/ 230 http_t http; 231 /* because http_internal_t* can be cast to http_t*. */ 232 233 void* memctx; 234 HTTP_SOCKET socket; 235 int connect_pending; 236 int request_sent; 237 char address[ 256 ]; 238 char request_header[ 256 ]; 239 char* request_header_large; 240 void* request_data; 241 size_t request_data_size; 242 char reason_phrase[ 1024 ]; 243 char content_type[ 256 ]; 244 size_t data_size; 245 size_t data_capacity; 246 void* data; 247 } http_internal_t; 248 249 250 static int http_internal_parse_url( char const* url, char* address, size_t address_capacity, char* port, 251 size_t port_capacity, char const** resource ) 252 { 253 // make sure url starts with http:// 254 if( strncmp( url, "http://", 7 ) != 0 ) return 0; 255 url += 7; // skip http:// part of url 256 257 size_t url_len = strlen( url ); 258 259 // find end of address part of url 260 char const* address_end = strchr( url, ':' ); 261 if( !address_end ) address_end = strchr( url, '/' ); 262 if( !address_end ) address_end = url + url_len; 263 264 // extract address 265 size_t address_len = (size_t)( address_end - url ); 266 if( address_len >= address_capacity ) return 0; 267 memcpy( address, url, address_len ); 268 address[ address_len ] = 0; 269 270 // check if there's a port defined 271 char const* port_end = address_end; 272 if( *address_end == ':' ) 273 { 274 ++address_end; 275 port_end = strchr( address_end, '/' ); 276 if( !port_end ) port_end = address_end + strlen( address_end ); 277 size_t port_len = (size_t)( port_end - address_end ); 278 if( port_len >= port_capacity ) return 0; 279 memcpy( port, address_end, port_len ); 280 port[ port_len ] = 0; 281 } 282 else 283 { 284 // use default port number 80 285 if( port_capacity <= 2 ) return 0; 286 strcpy( port, "80" ); 287 } 288 289 290 *resource = port_end; 291 292 return 1; 293 } 294 295 296 HTTP_SOCKET http_internal_connect( char const* address, char const* port ) 297 { 298 // set up hints for getaddrinfo 299 struct addrinfo hints; 300 memset( &hints, 0, sizeof( hints ) ); 301 hints.ai_family = AF_INET; // the Internet Protocol version 4 (IPv4) address family. 302 hints.ai_flags = AI_PASSIVE; 303 hints.ai_socktype = SOCK_STREAM; 304 hints.ai_protocol = IPPROTO_TCP; // Use Transmission Control Protocol (TCP). 305 306 // resolve the server address and port 307 struct addrinfo* addri = 0; 308 int error = getaddrinfo( address, port, &hints, &addri) ; 309 if( error != 0 ) return HTTP_INVALID_SOCKET; 310 311 // create the socket 312 HTTP_SOCKET sock = socket( addri->ai_family, addri->ai_socktype, addri->ai_protocol ); 313 if( sock == -1) 314 { 315 freeaddrinfo( addri ); 316 return HTTP_INVALID_SOCKET; 317 } 318 319 // set socket to nonblocking mode 320 u_long nonblocking = 1; 321 #ifdef _WIN32 322 int res = ioctlsocket( sock, FIONBIO, &nonblocking ); 323 #else 324 int flags = fcntl( sock, F_GETFL, 0 ); 325 int res = fcntl( sock, F_SETFL, flags | O_NONBLOCK ); 326 #endif 327 if( res == -1 ) 328 { 329 freeaddrinfo( addri ); 330 #ifdef _WIN32 331 closesocket( sock ); 332 #else 333 close( sock ); 334 #endif 335 return HTTP_INVALID_SOCKET; 336 } 337 338 // connect to server 339 if( connect( sock, addri->ai_addr, (int)addri->ai_addrlen ) == -1 ) 340 { 341 #ifdef _WIN32 342 if( WSAGetLastError() != WSAEWOULDBLOCK && WSAGetLastError() != WSAEINPROGRESS ) 343 { 344 freeaddrinfo( addri ); 345 closesocket( sock ); 346 return HTTP_INVALID_SOCKET; 347 } 348 #else 349 if( errno != EWOULDBLOCK && errno != EINPROGRESS && errno != EAGAIN ) 350 { 351 freeaddrinfo( addri ); 352 close( sock ); 353 return HTTP_INVALID_SOCKET; 354 } 355 #endif 356 } 357 358 freeaddrinfo( addri ); 359 return sock; 360 } 361 362 363 static http_internal_t* http_internal_create( size_t request_data_size, void* memctx ) 364 { 365 http_internal_t* internal = (http_internal_t*) HTTP_MALLOC( memctx, sizeof( http_internal_t ) + request_data_size ); 366 367 internal->http.status = HTTP_STATUS_PENDING; 368 internal->http.status_code = 0; 369 internal->http.response_size = 0; 370 internal->http.response_data = NULL; 371 372 internal->memctx = memctx; 373 internal->connect_pending = 1; 374 internal->request_sent = 0; 375 376 strcpy( internal->reason_phrase, "" ); 377 internal->http.reason_phrase = internal->reason_phrase; 378 379 strcpy( internal->content_type, "" ); 380 internal->http.content_type = internal->content_type; 381 382 internal->data_size = 0; 383 internal->data_capacity = 64 * 1024; 384 internal->data = HTTP_MALLOC( memctx, internal->data_capacity ); 385 386 internal->request_data = NULL; 387 internal->request_data_size = 0; 388 389 return internal; 390 } 391 392 393 http_t* http_get( char const* url, void* memctx ) 394 { 395 #ifdef _WIN32 396 WSADATA wsa_data; 397 if( WSAStartup( MAKEWORD( 1, 0 ), &wsa_data ) != 0 ) return NULL; 398 #endif 399 400 char address[ 256 ]; 401 char port[ 16 ]; 402 char const* resource; 403 404 if( http_internal_parse_url( url, address, sizeof( address ), port, sizeof( port ), &resource ) == 0 ) 405 return NULL; 406 407 HTTP_SOCKET socket = http_internal_connect( address, port ); 408 if( socket == HTTP_INVALID_SOCKET ) return NULL; 409 410 http_internal_t* internal = http_internal_create( 0, memctx ); 411 internal->socket = socket; 412 413 char* request_header; 414 size_t request_header_len = 64 + strlen( resource ) + strlen( address ) + strlen( port ); 415 if( request_header_len < sizeof( internal->request_header ) ) 416 { 417 internal->request_header_large = NULL; 418 request_header = internal->request_header; 419 } 420 else 421 { 422 internal->request_header_large = (char*) HTTP_MALLOC( memctx, request_header_len + 1 ); 423 request_header = internal->request_header_large; 424 } 425 int default_http_port = (strcmp(port, "80") == 0); 426 sprintf( request_header, "GET %s HTTP/1.0\r\nHost: %s%s%s\r\n\r\n", resource, address, default_http_port ? "" : ":", default_http_port ? "" : port ); 427 428 return &internal->http; 429 } 430 431 432 http_t* http_post( char const* url, void const* data, size_t size, void* memctx ) 433 { 434 #ifdef _WIN32 435 WSADATA wsa_data; 436 if( WSAStartup( MAKEWORD( 1, 0 ), &wsa_data ) != 0 ) return 0; 437 #endif 438 439 char address[ 256 ]; 440 char port[ 16 ]; 441 char const* resource; 442 443 if( http_internal_parse_url( url, address, sizeof( address ), port, sizeof( port ), &resource ) == 0 ) 444 return NULL; 445 446 HTTP_SOCKET socket = http_internal_connect( address, port ); 447 if( socket == HTTP_INVALID_SOCKET ) return NULL; 448 449 http_internal_t* internal = http_internal_create( size, memctx ); 450 internal->socket = socket; 451 452 char* request_header; 453 size_t request_header_len = 64 + strlen( resource ) + strlen( address ) + strlen( port ); 454 if( request_header_len < sizeof( internal->request_header ) ) 455 { 456 internal->request_header_large = NULL; 457 request_header = internal->request_header; 458 } 459 else 460 { 461 internal->request_header_large = (char*) HTTP_MALLOC( memctx, request_header_len + 1 ); 462 request_header = internal->request_header_large; 463 } 464 int default_http_port = (strcmp(port, "80") == 0); 465 sprintf( request_header, "POST %s HTTP/1.0\r\nHost: %s%s%s\r\nContent-Length: %d\r\n\r\n", resource, address, default_http_port ? "" : ":", default_http_port ? "" : port, 466 (int) size ); 467 468 internal->request_data_size = size; 469 internal->request_data = ( internal + 1 ); 470 memcpy( internal->request_data, data, size ); 471 472 return &internal->http; 473 } 474 475 476 http_status_t http_process( http_t* http ) 477 { 478 http_internal_t* internal = (http_internal_t*) http; 479 480 if( http->status == HTTP_STATUS_FAILED ) return http->status; 481 482 if( internal->connect_pending ) 483 { 484 fd_set sockets_to_check; 485 FD_ZERO( &sockets_to_check ); 486 #pragma warning( push ) 487 #pragma warning( disable: 4548 ) // expression before comma has no effect; expected expression with side-effect 488 FD_SET( internal->socket, &sockets_to_check ); 489 #pragma warning( pop ) 490 struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; 491 // check if socket is ready for send 492 if( select( (int)( internal->socket + 1 ), NULL, &sockets_to_check, NULL, &timeout ) == 1 ) 493 { 494 int opt = -1; 495 socklen_t len = sizeof( opt ); 496 if( getsockopt( internal->socket, SOL_SOCKET, SO_ERROR, (char*)( &opt ), &len) >= 0 && opt == 0 ) 497 internal->connect_pending = 0; // if it is, we're connected 498 } 499 } 500 501 if( internal->connect_pending ) return http->status; 502 503 if( !internal->request_sent ) 504 { 505 char const* request_header = internal->request_header_large ? 506 internal->request_header_large : internal->request_header; 507 if( send( internal->socket, request_header, (int) strlen( request_header ), 0 ) == -1 ) 508 { 509 http->status = HTTP_STATUS_FAILED; 510 return http->status; 511 } 512 if( internal->request_data_size ) 513 { 514 int res = send( internal->socket, (char const*)internal->request_data, (int) internal->request_data_size, 0 ); 515 if( res == -1 ) 516 { 517 http->status = HTTP_STATUS_FAILED; 518 return http->status; 519 } 520 } 521 internal->request_sent = 1; 522 return http->status; 523 } 524 525 // check if socket is ready for recv 526 fd_set sockets_to_check; 527 FD_ZERO( &sockets_to_check ); 528 #pragma warning( push ) 529 #pragma warning( disable: 4548 ) // expression before comma has no effect; expected expression with side-effect 530 FD_SET( internal->socket, &sockets_to_check ); 531 #pragma warning( pop ) 532 struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; 533 while( select( (int)( internal->socket + 1 ), &sockets_to_check, NULL, NULL, &timeout ) == 1 ) 534 { 535 char buffer[ 4096 ]; 536 int size = recv( internal->socket, buffer, sizeof( buffer ), 0 ); 537 if( size == -1 ) 538 { 539 http->status = HTTP_STATUS_FAILED; 540 return http->status; 541 } 542 else if( size > 0 ) 543 { 544 size_t min_size = internal->data_size + size + 1; 545 if( internal->data_capacity < min_size ) 546 { 547 internal->data_capacity *= 2; 548 if( internal->data_capacity < min_size ) internal->data_capacity = min_size; 549 void* new_data = HTTP_MALLOC( memctx, internal->data_capacity ); 550 memcpy( new_data, internal->data, internal->data_size ); 551 HTTP_FREE( memctx, internal->data ); 552 internal->data = new_data; 553 } 554 memcpy( (void*)( ( (uintptr_t) internal->data ) + internal->data_size ), buffer, (size_t) size ); 555 internal->data_size += size; 556 } 557 else if( size == 0 ) 558 { 559 char const* status_line = (char const*) internal->data; 560 561 int header_size = 0; 562 char const* header_end = strstr( status_line, "\r\n\r\n" ); 563 if( header_end ) 564 { 565 header_end += 4; 566 header_size = (int)( header_end - status_line ); 567 } 568 else 569 { 570 http->status = HTTP_STATUS_FAILED; 571 return http->status; 572 } 573 574 // skip http version 575 status_line = strchr( status_line, ' ' ); 576 if( !status_line ) 577 { 578 http->status = HTTP_STATUS_FAILED; 579 return http->status; 580 } 581 ++status_line; 582 583 // extract status code 584 char status_code[ 16 ]; 585 char const* status_code_end = strchr( status_line, ' ' ); 586 if( !status_code_end ) 587 { 588 http->status = HTTP_STATUS_FAILED; 589 return http->status; 590 } 591 memcpy( status_code, status_line, (size_t)( status_code_end - status_line ) ); 592 status_code[ status_code_end - status_line ] = 0; 593 status_line = status_code_end + 1; 594 http->status_code = atoi( status_code ); 595 596 // extract reason phrase 597 char const* reason_phrase_end = strstr( status_line, "\r\n" ); 598 if( !reason_phrase_end ) 599 { 600 http->status = HTTP_STATUS_FAILED; 601 return http->status; 602 } 603 size_t reason_phrase_len = (size_t)( reason_phrase_end - status_line ); 604 if( reason_phrase_len >= sizeof( internal->reason_phrase ) ) 605 reason_phrase_len = sizeof( internal->reason_phrase ) - 1; 606 memcpy( internal->reason_phrase, status_line, reason_phrase_len ); 607 internal->reason_phrase[ reason_phrase_len ] = 0; 608 status_line = reason_phrase_end + 1; 609 610 // extract content type 611 char const* content_type_start = strstr( status_line, "Content-Type: " ); 612 if( content_type_start ) 613 { 614 content_type_start += strlen( "Content-Type: " ); 615 char const* content_type_end = strstr( content_type_start, "\r\n" ); 616 if( content_type_end ) 617 { 618 size_t content_type_len = (size_t)( content_type_end - content_type_start ); 619 if( content_type_len >= sizeof( internal->content_type ) ) 620 content_type_len = sizeof( internal->content_type ) - 1; 621 memcpy( internal->content_type, content_type_start, content_type_len ); 622 internal->content_type[ content_type_len ] = 0; 623 } 624 } 625 626 http->status = http->status_code < 300 ? HTTP_STATUS_COMPLETED : HTTP_STATUS_FAILED; 627 http->response_data = (void*)( ( (uintptr_t) internal->data ) + header_size ); 628 http->response_size = internal->data_size - header_size; 629 630 // add an extra zero after the received data, but don't modify the size, so ascii results can be used as 631 // a zero terminated string. the size returned will be the string without this extra zero terminator. 632 ( (char*)http->response_data )[ http->response_size ] = 0; 633 return http->status; 634 } 635 } 636 637 return http->status; 638 } 639 640 641 void http_release( http_t* http ) 642 { 643 http_internal_t* internal = (http_internal_t*) http; 644 #ifdef _WIN32 645 closesocket( internal->socket ); 646 #else 647 close( internal->socket ); 648 #endif 649 650 if( internal->request_header_large) HTTP_FREE( memctx, internal->request_header_large ); 651 HTTP_FREE( memctx, internal->data ); 652 HTTP_FREE( memctx, internal ); 653 #ifdef _WIN32 654 WSACleanup(); 655 #endif 656 } 657 658 659 #endif /* HTTP_IMPLEMENTATION */ 660 661 /* 662 revision history: 663 1.0 first released version 664 */ 665 666 /* 667 ------------------------------------------------------------------------------ 668 669 This software is available under 2 licenses - you may choose the one you like. 670 671 ------------------------------------------------------------------------------ 672 673 ALTERNATIVE A - MIT License 674 675 Copyright (c) 2016 Mattias Gustavsson 676 677 Permission is hereby granted, free of charge, to any person obtaining a copy of 678 this software and associated documentation files (the "Software"), to deal in 679 the Software without restriction, including without limitation the rights to 680 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 681 of the Software, and to permit persons to whom the Software is furnished to do 682 so, subject to the following conditions: 683 684 The above copyright notice and this permission notice shall be included in all 685 copies or substantial portions of the Software. 686 687 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 688 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 689 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 690 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 691 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 692 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 693 SOFTWARE. 694 695 ------------------------------------------------------------------------------ 696 697 ALTERNATIVE B - Public Domain (www.unlicense.org) 698 699 This is free and unencumbered software released into the public domain. 700 701 Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 702 software, either in source code form or as a compiled binary, for any purpose, 703 commercial or non-commercial, and by any means. 704 705 In jurisdictions that recognize copyright laws, the author or authors of this 706 software dedicate any and all copyright interest in the software to the public 707 domain. We make this dedication for the benefit of the public at large and to 708 the detriment of our heirs and successors. We intend this dedication to be an 709 overt act of relinquishment in perpetuity of all present and future rights to 710 this software under copyright law. 711 712 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 713 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 714 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 715 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 716 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 717 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 718 719 ------------------------------------------------------------------------------ 720 */