diff --git a/bin/webs.c b/bin/webs.c index 08cb3fa..1d8b2a6 100644 --- a/bin/webs.c +++ b/bin/webs.c @@ -76,7 +76,6 @@ static int process_http_request (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_ mth = hio_htre_getqmethodtype(req); qpath = hio_htre_getqpath(req); - if (mth == HIO_HTTP_OTHER && hio_comp_bcstr(hio_htre_getqmethodname(req), "UNTAR", 1) == 0) { /* don't care about the path for now. TODO: make this secure and reasonable */ @@ -85,11 +84,7 @@ static int process_http_request (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_ else // if (mth == HIO_HTTP_GET || mth == HIO_HTTP_POST) { /* TODO: proper mime-type */ - const hio_bch_t* dot; - hio_bch_t mt[128]; - dot = hio_rfind_bchar_in_bcstr(qpath, '.'); - hio_fmttobcstr (hio, mt, HIO_COUNTOF(mt), "text/%hs", ((dot && dot[1] != '\0')? &dot[1]: "html")); /* TODO: error check */ - if (hio_svc_htts_dofile(htts, csck, req, ext->docroot, qpath, mt, 0) <= -1) goto oops; + if (hio_svc_htts_dofile(htts, csck, req, ext->docroot, qpath, HIO_NULL, 0) <= -1) goto oops; } #if 0 else @@ -152,12 +147,12 @@ static void handle_sigint (int sig) int main (int argc, char* argv[]) { hio_t* hio = HIO_NULL; - hio_oow_t i; struct sigaction sigact; int xret = -1; #if 0 - + hio_oow_t i; + // TODO: use getopt() or something similar for (i = 1; i < argc; ) { diff --git a/lib/Makefile.am b/lib/Makefile.am index 375ff55..8c70cf7 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -118,6 +118,7 @@ libhio_la_SOURCES = \ tmr.c \ utf8.c \ utl.c \ + utl-mime.c \ utl-siph.c \ utl-str.c diff --git a/lib/Makefile.in b/lib/Makefile.in index 8d5fc58..7b15bb3 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -154,7 +154,8 @@ am__libhio_la_SOURCES_DIST = chr.c dhcp-svr.c dns.c dns-cli.c ecs.c \ nwif.c opt.c opt-imp.h path.c pipe.c pro.c pty.c rad-msg.c \ sck.c shw.c skad.c sys.c sys-ass.c sys-err.c sys-log.c \ sys-mux.c sys-prv.h sys-tim.c thr.c uch-case.h uch-prop.h \ - tar.c tmr.c utf8.c utl.c utl-siph.c utl-str.c mar.c mar-cli.c + tar.c tmr.c utf8.c utl.c utl-mime.c utl-siph.c utl-str.c mar.c \ + mar-cli.c @ENABLE_MARIADB_TRUE@am__objects_1 = libhio_la-mar.lo \ @ENABLE_MARIADB_TRUE@ libhio_la-mar-cli.lo am_libhio_la_OBJECTS = libhio_la-chr.lo libhio_la-dhcp-svr.lo \ @@ -171,8 +172,8 @@ am_libhio_la_OBJECTS = libhio_la-chr.lo libhio_la-dhcp-svr.lo \ libhio_la-sys.lo libhio_la-sys-ass.lo libhio_la-sys-err.lo \ libhio_la-sys-log.lo libhio_la-sys-mux.lo libhio_la-sys-tim.lo \ libhio_la-thr.lo libhio_la-tar.lo libhio_la-tmr.lo \ - libhio_la-utf8.lo libhio_la-utl.lo libhio_la-utl-siph.lo \ - libhio_la-utl-str.lo $(am__objects_1) + libhio_la-utf8.lo libhio_la-utl.lo libhio_la-utl-mime.lo \ + libhio_la-utl-siph.lo libhio_la-utl-str.lo $(am__objects_1) libhio_la_OBJECTS = $(am_libhio_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -228,6 +229,7 @@ am__depfiles_remade = ./$(DEPDIR)/libhio_la-chr.Plo \ ./$(DEPDIR)/libhio_la-sys.Plo ./$(DEPDIR)/libhio_la-tar.Plo \ ./$(DEPDIR)/libhio_la-thr.Plo ./$(DEPDIR)/libhio_la-tmr.Plo \ ./$(DEPDIR)/libhio_la-utf8.Plo \ + ./$(DEPDIR)/libhio_la-utl-mime.Plo \ ./$(DEPDIR)/libhio_la-utl-siph.Plo \ ./$(DEPDIR)/libhio_la-utl-str.Plo \ ./$(DEPDIR)/libhio_la-utl.Plo @@ -474,7 +476,7 @@ libhio_la_SOURCES = chr.c dhcp-svr.c dns.c dns-cli.c ecs.c ecs-imp.h \ opt.c opt-imp.h path.c pipe.c pro.c pty.c rad-msg.c sck.c \ shw.c skad.c sys.c sys-ass.c sys-err.c sys-log.c sys-mux.c \ sys-prv.h sys-tim.c thr.c uch-case.h uch-prop.h tar.c tmr.c \ - utf8.c utl.c utl-siph.c utl-str.c $(am__append_2) + utf8.c utl.c utl-mime.c utl-siph.c utl-str.c $(am__append_2) libhio_la_CPPFLAGS = $(CPPFLAGS_LIB_COMMON) libhio_la_CFLAGS = $(CFLAGS_LIB_COMMON) $(am__append_3) libhio_la_LDFLAGS = $(LDFLAGS_LIB_COMMON) $(am__append_4) @@ -617,6 +619,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhio_la-thr.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhio_la-tmr.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhio_la-utf8.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhio_la-utl-mime.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhio_la-utl-siph.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhio_la-utl-str.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhio_la-utl.Plo@am__quote@ # am--include-marker @@ -945,6 +948,13 @@ libhio_la-utl.lo: utl.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhio_la_CPPFLAGS) $(CPPFLAGS) $(libhio_la_CFLAGS) $(CFLAGS) -c -o libhio_la-utl.lo `test -f 'utl.c' || echo '$(srcdir)/'`utl.c +libhio_la-utl-mime.lo: utl-mime.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhio_la_CPPFLAGS) $(CPPFLAGS) $(libhio_la_CFLAGS) $(CFLAGS) -MT libhio_la-utl-mime.lo -MD -MP -MF $(DEPDIR)/libhio_la-utl-mime.Tpo -c -o libhio_la-utl-mime.lo `test -f 'utl-mime.c' || echo '$(srcdir)/'`utl-mime.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhio_la-utl-mime.Tpo $(DEPDIR)/libhio_la-utl-mime.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utl-mime.c' object='libhio_la-utl-mime.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhio_la_CPPFLAGS) $(CPPFLAGS) $(libhio_la_CFLAGS) $(CFLAGS) -c -o libhio_la-utl-mime.lo `test -f 'utl-mime.c' || echo '$(srcdir)/'`utl-mime.c + libhio_la-utl-siph.lo: utl-siph.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhio_la_CPPFLAGS) $(CPPFLAGS) $(libhio_la_CFLAGS) $(CFLAGS) -MT libhio_la-utl-siph.lo -MD -MP -MF $(DEPDIR)/libhio_la-utl-siph.Tpo -c -o libhio_la-utl-siph.lo `test -f 'utl-siph.c' || echo '$(srcdir)/'`utl-siph.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhio_la-utl-siph.Tpo $(DEPDIR)/libhio_la-utl-siph.Plo @@ -1175,6 +1185,7 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/libhio_la-thr.Plo -rm -f ./$(DEPDIR)/libhio_la-tmr.Plo -rm -f ./$(DEPDIR)/libhio_la-utf8.Plo + -rm -f ./$(DEPDIR)/libhio_la-utl-mime.Plo -rm -f ./$(DEPDIR)/libhio_la-utl-siph.Plo -rm -f ./$(DEPDIR)/libhio_la-utl-str.Plo -rm -f ./$(DEPDIR)/libhio_la-utl.Plo @@ -1267,6 +1278,7 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/libhio_la-thr.Plo -rm -f ./$(DEPDIR)/libhio_la-tmr.Plo -rm -f ./$(DEPDIR)/libhio_la-utf8.Plo + -rm -f ./$(DEPDIR)/libhio_la-utl-mime.Plo -rm -f ./$(DEPDIR)/libhio_la-utl-siph.Plo -rm -f ./$(DEPDIR)/libhio_la-utl-str.Plo -rm -f ./$(DEPDIR)/libhio_la-utl.Plo diff --git a/lib/hio-utl.h b/lib/hio-utl.h index 1554957..52c7b5b 100644 --- a/lib/hio-utl.h +++ b/lib/hio-utl.h @@ -224,6 +224,22 @@ #define HIO_HASH_UCSTR(hv, ptr) HIO_HASH_VPTR(hv, ptr, const hio_uch_t) #define HIO_HASH_MORE_UCSTR(hv, ptr) HIO_HASH_MORE_VPTR(hv, ptr, const hio_uch_t) + +/* ========================================================================= + * MIME TYPE ENTRY + * ========================================================================= */ +struct hio_mime_type_t +{ + const hio_bch_t* ext; + const hio_bch_t* type; +}; + +typedef struct hio_mime_type_t hio_mime_type_t; + +#if defined(__cplusplus) +extern "C" { +#endif + /* ========================================================================= * STRING * ========================================================================= */ @@ -652,7 +668,6 @@ static HIO_INLINE hio_uint128_t hio_bswap128 (hio_uint128_t x) # error UNKNOWN ENDIAN #endif - /* ========================================================================= * SIP-HASH-PRF * ========================================================================= */ @@ -663,4 +678,15 @@ HIO_EXPORT void hio_sip_hash_24 ( hio_uint8_t out[8] ); +/* ========================================================================= + * mime-type by extension + * ========================================================================= */ +HIO_EXPORT const hio_bch_t* hio_get_mime_type_by_ext ( + const hio_bch_t* ext +); + +#if defined(__cplusplus) +} +#endif + #endif diff --git a/lib/http-file.c b/lib/http-file.c index d707910..10c4bad 100644 --- a/lib/http-file.c +++ b/lib/http-file.c @@ -479,12 +479,16 @@ static int file_send_header_to_client (file_t* file, int status_code, int force_ content_length = file->end_offset - file->start_offset + 1; if (status_code == HIO_HTTP_STATUS_OK && file->total_size != content_length) status_code = HIO_HTTP_STATUS_PARTIAL_CONTENT; - if (hio_becs_fmt(cli->sbuf, "HTTP/%d.%d %d %hs\r\nServer: %hs\r\nDate: %s\r\nConnection: %hs\r\nAccept-Ranges: bytes\r\nContent-Type: %hs\r\n", + if (hio_becs_fmt(cli->sbuf, "HTTP/%d.%d %d %hs\r\nServer: %hs\r\nDate: %s\r\nConnection: %hs\r\nAccept-Ranges: bytes\r\n", file->req_version.major, file->req_version.minor, status_code, hio_http_status_to_bcstr(status_code), cli->htts->server_name, dtbuf, - (force_close? "close": "keep-alive"), mime_type) == (hio_oow_t)-1) return -1; + (force_close? "close": "keep-alive")) == (hio_oow_t)-1) return -1; + /* Content-Type is not set if mime_type is null or blank */ + if (mime_type && mime_type[0] != '\0' && + hio_becs_fcat(cli->sbuf, "Content-Type: %hs\r\n", mime_type) == (hio_oow_t)-1) return -1; + if (file->req_method == HIO_HTTP_GET && hio_becs_fcat(cli->sbuf, "ETag: %hs\r\n", file->peer_etag) == (hio_oow_t)-1) return -1; if (status_code == HIO_HTTP_STATUS_PARTIAL_CONTENT && hio_becs_fcat(cli->sbuf, "Content-Ranges: bytes %ju-%ju/%ju\r\n", (hio_uintmax_t)file->start_offset, (hio_uintmax_t)file->end_offset, (hio_uintmax_t)file->total_size) == (hio_oow_t)-1) return -1; @@ -672,9 +676,11 @@ static HIO_INLINE int process_range_header (file_t* file, hio_htre_t* req, int* return 0; } -static int open_peer_with_mode (file_t* file, const hio_bch_t* actual_file, int flags, int* error_status) +static int open_peer_with_mode (file_t* file, const hio_bch_t* actual_file, int flags, int* error_status, const hio_bch_t** actual_mime_type) { struct stat st; + const hio_bch_t* opened_file; + flags |= O_NONBLOCK; #if defined(O_CLOEXEC) @@ -691,7 +697,8 @@ static int open_peer_with_mode (file_t* file, const hio_bch_t* actual_file, int return -1; } - if (fstat(file->peer, &st) >= 0 && S_ISDIR(st.st_mode)) + opened_file = actual_file; + if ((flags & O_RDONLY) && fstat(file->peer, &st) >= 0 && S_ISDIR(st.st_mode)) /* only for read operation */ { hio_bch_t* alt_file; int alt_fd; @@ -701,14 +708,30 @@ static int open_peer_with_mode (file_t* file, const hio_bch_t* actual_file, int if (alt_file) { alt_fd = open(alt_file, flags, 0644); - hio_freemem (file->htts->hio, alt_file); if (alt_fd >= 0) { close (file->peer); file->peer = alt_fd; + opened_file = alt_file; } } } + + if (actual_mime_type) + { + const hio_bch_t* dot; + dot = hio_rfind_bchar_in_bcstr(opened_file, '.'); + if (dot) + { + const hio_bch_t* mt; + mt = hio_get_mime_type_by_ext(dot + 1); + if (mt) *actual_mime_type = mt; + } + } + + if (opened_file != actual_file) + hio_freemem (file->htts->hio, (hio_bch_t*)opened_file); + return 0; } @@ -854,7 +877,10 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* switch (file->req_method) { case HIO_HTTP_GET: - if (open_peer_with_mode(file, actual_file, O_RDONLY, &status_code) <= -1) goto done_with_status_code; + { + const hio_bch_t* actual_mime_type = mime_type; + + if (open_peer_with_mode(file, actual_file, O_RDONLY, &status_code, (mime_type? HIO_NULL: &actual_mime_type)) <= -1) goto done_with_status_code; if (process_range_header(file, req, &status_code) <= -1) goto done_with_status_code; if (file->etag_match) @@ -869,13 +895,14 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* #endif set_tcp_cork (file->client->sck); - if (file_send_header_to_client(file, HIO_HTTP_STATUS_OK, 0, mime_type) <= -1 || + if (file_send_header_to_client(file, HIO_HTTP_STATUS_OK, 0, actual_mime_type) <= -1 || file_send_contents_to_client(file) <= -1) goto oops; break; + } case HIO_HTTP_HEAD: - if (open_peer_with_mode(file, actual_file, O_RDONLY, &status_code) <= -1) goto done_with_status_code; + if (open_peer_with_mode(file, actual_file, O_RDONLY, &status_code, HIO_NULL) <= -1) goto done_with_status_code; if (process_range_header(file, req, &status_code) <= -1) goto done_with_status_code; status_code = HIO_HTTP_STATUS_OK; goto done_with_status_code; @@ -888,7 +915,7 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* goto done_with_status_code; } - if (open_peer_with_mode(file, actual_file, O_WRONLY | O_TRUNC | O_CREAT, &status_code) <= -1) goto done_with_status_code; + if (open_peer_with_mode(file, actual_file, O_WRONLY | O_TRUNC | O_CREAT, &status_code, HIO_NULL) <= -1) goto done_with_status_code; /* the client input must be written to the peer side */ file_mark_over (file, FILE_OVER_READ_FROM_PEER); diff --git a/lib/utl-mime.c b/lib/utl-mime.c new file mode 100644 index 0000000..6bf1348 --- /dev/null +++ b/lib/utl-mime.c @@ -0,0 +1,139 @@ +/* + Copyright (c) 2016-2020 Chung, Hyung-Hwan. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +/* Keep this table sorted for binary search */ +static hio_mime_type_t mime_type_tab[] = +{ + {"3gp", "video/3gpp"}, + {"3g2", "video/3gpp2"}, + {"7z", "application/x-7z-compressed"}, + {"aac", "audio/aac"}, + {"abw", "application/x-abiword"}, + {"arc", "application/x-freearc"}, + {"avif", "image/avif"}, + {"avi", "video/x-msvideo"}, + {"azw", "application/vnd.amazon.ebook"}, + {"bin", "application/octet-stream"}, + {"bmp", "image/bmp"}, + {"bz", "application/x-bzip"}, + {"bz2", "application/x-bzip2"}, + {"cda", "application/x-cdf"}, + {"csh", "application/x-csh"}, + {"css", "text/css"}, + {"csv", "text/csv"}, + {"doc", "application/msword"}, + {"docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, + {"eot", "application/vnd.ms-fontobject"}, + {"epub", "application/epub+zip"}, + {"gz", "application/gzip"}, + {"gif", "image/gif"}, + {"htm", "text/html"}, + {"html", "text/html"}, + {"ico", "image/vnd.microsoft.icon"}, + {"ics", "text/calendar"}, + {"jar", "application/java-archive"}, + {"jpeg", "image/jpeg"}, + {"jpg", "image/jpeg"}, + {"js", "text/javascript"}, + {"json", "application/json"}, + {"jsonld", "application/ld+json"}, + {"mid", "audio/midi"}, + {"midi", "audio/midi"}, + {"mjs", "text/javascript"}, + {"mp3", "audio/mpeg"}, + {"mp4", "video/mp4"}, + {"mpeg", "video/mpeg"}, + {"mpkg", "application/vnd.apple.installer+xml"}, + {"odp", "application/vnd.oasis.opendocument.presentation"}, + {"ods", "application/vnd.oasis.opendocument.spreadsheet"}, + {"odt", "application/vnd.oasis.opendocument.text"}, + {"oga", "audio/ogg"}, + {"ogv", "video/ogg"}, + {"ogx", "application/ogg"}, + {"opus", "audio/opus"}, + {"otf", "font/otf"}, + {"png", "image/png"}, + {"pdf", "application/pdf"}, + {"php", "application/x-httpd-php"}, + {"ppt", "application/vnd.ms-powerpoint"}, + {"pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, + {"rar", "application/vnd.rar"}, + {"rtf", "application/rtf"}, + {"sh", "application/x-sh"}, + {"svg", "image/svg+xml"}, + {"tar", "application/x-tar"}, + {"tif", "image/tiff"}, + {"tiff", "image/tiff"}, + {"ts", "video/mp2t"}, + {"ttf", "font/ttf"}, + {"txt", "text/plain"}, + {"vsd", "application/vnd.visio"}, + {"wav", "audio/wav"}, + {"weba", "audio/webm"}, + {"webm", "video/webm"}, + {"webp", "image/webp"}, + {"woff", "font/woff"}, + {"woff2", "font/woff2"}, + {"xhtml", "application/xhtml+xml"}, + {"xls", "application/vnd.ms-excel"}, + {"xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, + {"xml", "application/xml"}, + {"xul", "application/vnd.mozilla.xul+xml"}, + {"zip", "application/zip"} +}; + +const hio_bch_t* hio_get_mime_type_by_ext(const hio_bch_t* ext) +{ + /* perform binary search */ + + /* declaring left, right, mid to be of int is ok + * because we know mtab is small enough. */ + int left = 0, right = HIO_COUNTOF(mime_type_tab) - 1, mid; + + while (left <= right) + { + int n; + hio_mime_type_t* entry; + + /*mid = (left + right) / 2;*/ + mid = left + (right - left) / 2; + entry = &mime_type_tab[mid]; + + n = hio_comp_bcstr(ext, entry->ext, 1); + if (n < 0) + { + /* if left, right, mid were of hio_oow_t, + * you would need the following line. + if (mid == 0) break; + */ + right = mid - 1; + } + else if (n > 0) left = mid + 1; + else return entry->type; + } + + return HIO_NULL; +}