00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 #include "SpdyCallbacks.h"
00025 #include "SpdyClientSession.h"
00026 #include <arpa/inet.h>
00027 
00028 void
00029 spdy_callbacks_init(spdylay_session_callbacks *callbacks)
00030 {
00031   memset(callbacks, 0, sizeof(spdylay_session_callbacks));
00032 
00033   callbacks->send_callback = spdy_send_callback;
00034   callbacks->recv_callback = spdy_recv_callback;
00035   callbacks->on_ctrl_recv_callback = spdy_on_ctrl_recv_callback;
00036   callbacks->on_invalid_ctrl_recv_callback = spdy_on_invalid_ctrl_recv_callback;
00037   callbacks->on_data_chunk_recv_callback = spdy_on_data_chunk_recv_callback;
00038   callbacks->on_data_recv_callback = spdy_on_data_recv_callback;
00039   callbacks->before_ctrl_send_callback = spdy_before_ctrl_send_callback;
00040   callbacks->on_ctrl_send_callback = spdy_on_ctrl_send_callback;
00041   callbacks->on_ctrl_not_send_callback = spdy_on_ctrl_not_send_callback;
00042   callbacks->on_data_send_callback = spdy_on_data_send_callback;
00043   callbacks->on_stream_close_callback = spdy_on_stream_close_callback;
00044   callbacks->on_request_recv_callback = spdy_on_request_recv_callback;
00045   callbacks->get_credential_proof = spdy_get_credential_proof;
00046   callbacks->get_credential_ncerts = spdy_get_credential_ncerts;
00047   callbacks->get_credential_cert = spdy_get_credential_cert;
00048   callbacks->on_ctrl_recv_parse_error_callback = spdy_on_ctrl_recv_parse_error_callback;
00049   callbacks->on_unknown_ctrl_recv_callback = spdy_on_unknown_ctrl_recv_callback;
00050 }
00051 
00052 void
00053 spdy_prepare_status_response_and_clean_request(SpdyClientSession *sm, int stream_id, const char *status)
00054 {
00055   SpdyRequest *req = sm->req_map[stream_id];
00056   string date_str = http_date(time(0));
00057   const char **nv = new const char*[8+req->headers.size()*2+1];
00058 
00059   nv[0] = ":status";
00060   nv[1] = status;
00061   nv[2] = ":version";
00062   nv[3] = "HTTP/1.1";
00063   nv[4] = "server";
00064   nv[5] = SPDYD_SERVER;
00065   nv[6] = "date";
00066   nv[7] = date_str.c_str();
00067 
00068   for(size_t i = 0; i < req->headers.size(); ++i) {
00069     nv[8+i*2] = req->headers[i].first.c_str();
00070     nv[8+i*2+1] = req->headers[i].second.c_str();
00071   }
00072   nv[8+req->headers.size()*2] = 0;
00073 
00074   int r = spdylay_submit_response(sm->session, stream_id, nv, NULL);
00075   TSAssert(r == 0);
00076 
00077   TSVIOReenable(sm->write_vio);
00078   delete [] nv;
00079   sm->cleanup_request(stream_id);
00080 }
00081 
00082 static void
00083 spdy_show_data_frame(const char *head_str, spdylay_session * , uint8_t flags,
00084                      int32_t stream_id, int32_t length, void *user_data)
00085 {
00086   if (!is_debug_tag_set("spdy"))
00087     return;
00088 
00089   SpdyClientSession *sm = (SpdyClientSession *)user_data;
00090 
00091   Debug("spdy", "%s DATA frame (sm_id:%" PRIu64 ", stream_id:%d, flag:%d, length:%d)",
00092         head_str, sm->sm_id, stream_id, flags, length);
00093 }
00094 
00095 static void
00096 spdy_show_ctl_frame(const char *head_str, spdylay_session * , spdylay_frame_type type,
00097                     spdylay_frame *frame, void *user_data)
00098 {
00099   if (!is_debug_tag_set("spdy"))
00100     return;
00101 
00102   SpdyClientSession *sm = (SpdyClientSession *)user_data;
00103   switch (type) {
00104   case SPDYLAY_SYN_STREAM: {
00105     spdylay_syn_stream *f = (spdylay_syn_stream *)frame;
00106     Debug("spdy", "%s SYN_STREAM (sm_id:%" PRIu64 ", stream_id:%d, flag:%d, length:%d)",
00107           head_str, sm->sm_id, f->stream_id, f->hd.flags, f->hd.length);
00108     int j, i;
00109     j = i = 0;
00110     while (f->nv[j]) {
00111       Debug("spdy", "    %s: %s", f->nv[j], f->nv[j+1]);
00112       i++;
00113       j = 2*i;
00114     }
00115   }
00116     break;
00117   case SPDYLAY_SYN_REPLY: {
00118     spdylay_syn_reply *f = (spdylay_syn_reply *)frame;
00119     Debug("spdy", "%s SYN_REPLY (sm_id:%" PRIu64 ", stream_id:%d, flag:%d, length:%d)",
00120           head_str, sm->sm_id, f->stream_id, f->hd.flags, f->hd.length);
00121     int j, i;
00122     j = i = 0;
00123     while (f->nv[j]) {
00124       Debug("spdy", "    %s: %s", f->nv[j], f->nv[j+1]);
00125       i++;
00126       j = 2*i;
00127     }
00128   }
00129     break;
00130   case SPDYLAY_WINDOW_UPDATE: {
00131     spdylay_window_update *f = (spdylay_window_update *)frame;
00132     Debug("spdy", "%s WINDOW_UPDATE (sm_id:%" PRIu64 ", stream_id:%d, flag:%d, delta_window_size:%u)",
00133           head_str, sm->sm_id, f->stream_id, f->hd.flags, f->delta_window_size);
00134   }
00135     break;
00136   case SPDYLAY_SETTINGS: {
00137     spdylay_settings *f = (spdylay_settings *)frame;
00138     Debug("spdy", "%s SETTINGS frame (sm_id:%" PRIu64 ", flag:%d, length:%d, niv:%zu)",
00139           head_str, sm->sm_id, f->hd.flags, f->hd.length, f->niv);
00140     for (size_t i = 0; i < f->niv; i++) {
00141       Debug("spdy", "    (%d:%d)", f->iv[i].settings_id, f->iv[i].value);
00142     }
00143   }
00144     break;
00145   case SPDYLAY_HEADERS: {
00146     spdylay_headers *f = (spdylay_headers *)frame;
00147     Debug("spdy", "%s HEADERS frame (sm_id:%" PRIu64 ", stream_id:%d, flag:%d, length:%d)",
00148           head_str, sm->sm_id, f->stream_id, f->hd.flags, f->hd.length);
00149   }
00150     break;
00151   case SPDYLAY_RST_STREAM: {
00152     spdylay_rst_stream *f = (spdylay_rst_stream *)frame;
00153     Debug("spdy", "%s RST_STREAM (sm_id:%" PRIu64 ", stream_id:%d, flag:%d, length:%d, code:%d)",
00154           head_str, sm->sm_id, f->stream_id, f->hd.flags, f->hd.length, f->status_code);
00155   }
00156     break;
00157   case SPDYLAY_GOAWAY: {
00158     spdylay_goaway *f = (spdylay_goaway *)frame;
00159     Debug("spdy", "%s GOAWAY frame (sm_id:%" PRIu64 ", last_good_stream_id:%d, flag:%d, length:%d",
00160           head_str, sm->sm_id, f->last_good_stream_id, f->hd.flags, f->hd.length);
00161   }
00162   default:
00163     break;
00164   }
00165   return;
00166 }
00167 
00168 static int
00169 spdy_fetcher_launch(SpdyRequest *req)
00170 {
00171   string url;
00172   int fetch_flags;
00173   const sockaddr *client_addr;
00174   SpdyClientSession *sm = req->spdy_sm;
00175 
00176   url = req->scheme + "://" + req->host + req->path;
00177   client_addr = TSNetVConnRemoteAddrGet(reinterpret_cast<TSVConn>(sm->vc));
00178 
00179   req->url = url;
00180   Debug("spdy", "++++Request[%" PRIu64 ":%d] %s", sm->sm_id, req->stream_id, req->url.c_str());
00181 
00182   
00183   
00184   
00185   fetch_flags = TS_FETCH_FLAGS_DECHUNK;
00186 
00187   
00188   fetch_flags |= TS_FETCH_FLAGS_NOT_INTERNAL_REQUEST;
00189 
00190   req->fetch_sm = TSFetchCreate((TSCont)sm, req->method.c_str(),
00191                                 url.c_str(), req->version.c_str(),
00192                                 client_addr, fetch_flags);
00193   TSFetchUserDataSet(req->fetch_sm, req);
00194 
00195   
00196   
00197   
00198   for (size_t i = 0; i < req->headers.size(); i++) {
00199 
00200     if (*req->headers[i].first.c_str() == ':')
00201       continue;
00202 
00203     TSFetchHeaderAdd(req->fetch_sm,
00204                      req->headers[i].first.c_str(), req->headers[i].first.size(),
00205                      req->headers[i].second.c_str(), req->headers[i].second.size());
00206   }
00207 
00208   TSFetchLaunch(req->fetch_sm);
00209   return 0;
00210 }
00211 
00212 ssize_t
00213 spdy_send_callback(spdylay_session * , const uint8_t *data, size_t length,
00214                    int , void *user_data)
00215 {
00216   SpdyClientSession  *sm = (SpdyClientSession*)user_data;
00217 
00218   sm->total_size += length;
00219   TSIOBufferWrite(sm->resp_buffer, data, length);
00220 
00221   Debug("spdy", "----spdy_send_callback, length:%zu", length);
00222 
00223   return length;
00224 }
00225 
00226 ssize_t
00227 spdy_recv_callback(spdylay_session * , uint8_t *buf, size_t length,
00228                    int , void *user_data)
00229 {
00230   const char *start;
00231   TSIOBufferBlock blk, next_blk;
00232   int64_t already, blk_len, need, wavail;
00233 
00234   SpdyClientSession  *sm = (SpdyClientSession*)user_data;
00235 
00236   already = 0;
00237   blk = TSIOBufferReaderStart(sm->req_reader);
00238 
00239   while (blk) {
00240 
00241     wavail = length - already;
00242 
00243     next_blk = TSIOBufferBlockNext(blk);
00244     start = TSIOBufferBlockReadStart(blk, sm->req_reader, &blk_len);
00245 
00246     need = blk_len > wavail ? wavail : blk_len;
00247 
00248     memcpy(&buf[already], start, need);
00249     already += need;
00250 
00251     if (already >= (int64_t)length)
00252       break;
00253 
00254     blk = next_blk;
00255   }
00256 
00257   TSIOBufferReaderConsume(sm->req_reader, already);
00258 
00259   
00260   
00261   if (sm->read_vio) {
00262     TSVIOReenable(sm->read_vio);
00263   }
00264 
00265   if (!already)
00266     return SPDYLAY_ERR_WOULDBLOCK;
00267 
00268   return already;
00269 }
00270 
00271 static void
00272 spdy_process_syn_stream_frame(SpdyClientSession *sm, SpdyRequest *req)
00273 {
00274   bool acceptEncodingRecvd = false;
00275   
00276   for(size_t i = 0; i < req->headers.size(); ++i) {
00277     const std::string &field = req->headers[i].first;
00278     const std::string &value = req->headers[i].second;
00279 
00280     if(field == ":path")
00281       req->path = value;
00282     else if(field == ":method")
00283       req->method = value;
00284     else if(field == ":scheme")
00285       req->scheme = value;
00286     else if(field == ":version")
00287       req->version = value;
00288     else if(field == ":host")
00289       req->host = value;
00290     else if(field == "accept-encoding")
00291       acceptEncodingRecvd = true;
00292   }
00293 
00294   if(!req->path.size()|| !req->method.size() || !req->scheme.size()
00295      || !req->version.size() || !req->host.size()) {
00296     spdy_prepare_status_response_and_clean_request(sm, req->stream_id, STATUS_400);
00297     return;
00298   }
00299 
00300   if (!acceptEncodingRecvd) {
00301     Debug("spdy", "Accept-Encoding header not received, adding gzip for method %s", req->method.c_str());
00302     req->headers.push_back(make_pair("accept-encoding", "gzip, deflate"));
00303   }
00304 
00305   spdy_fetcher_launch(req);
00306 }
00307 
00308 void
00309 spdy_on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
00310                            spdylay_frame *frame, void *user_data)
00311 {
00312   int         stream_id;
00313   SpdyRequest *req;
00314   SpdyClientSession      *sm = (SpdyClientSession*)user_data;
00315 
00316   spdy_show_ctl_frame("++++RECV", session, type, frame, user_data);
00317 
00318   switch (type) {
00319 
00320   case SPDYLAY_SYN_STREAM:
00321     stream_id = frame->syn_stream.stream_id;
00322     req = spdyRequestAllocator.alloc();
00323     req->init(sm, stream_id);
00324     req->append_nv(frame->syn_stream.nv);
00325     sm->req_map[stream_id] = req;
00326     spdy_process_syn_stream_frame(sm, req);
00327     break;
00328 
00329   case SPDYLAY_HEADERS:
00330     stream_id = frame->syn_stream.stream_id;
00331     req = sm->req_map[stream_id];
00332     req->append_nv(frame->headers.nv);
00333     break;
00334 
00335   case SPDYLAY_WINDOW_UPDATE:
00336     TSVIOReenable(sm->write_vio);
00337     break;
00338 
00339   default:
00340     break;
00341   }
00342   return;
00343 }
00344 
00345 void
00346 spdy_on_invalid_ctrl_recv_callback(spdylay_session * ,
00347                                    spdylay_frame_type ,
00348                                    spdylay_frame * ,
00349                                    uint32_t ,
00350                                    void * )
00351 {
00352   
00353   return;
00354 }
00355 
00356 void
00357 spdy_on_data_chunk_recv_callback(spdylay_session * , uint8_t ,
00358                                  int32_t stream_id, const uint8_t *data,
00359                                  size_t len, void *user_data)
00360 {
00361   SpdyClientSession *sm = (SpdyClientSession *)user_data;
00362   SpdyRequest *req = sm->find_request(stream_id);
00363 
00364   
00365   
00366   
00367   if (!req)
00368     return;
00369 
00370   Debug("spdy", "++++Fetcher Append Data, len:%zu", len);
00371   TSFetchWriteData(req->fetch_sm, data, len);
00372 
00373   return;
00374 }
00375 
00376 void
00377 spdy_on_data_recv_callback(spdylay_session *session, uint8_t flags,
00378                            int32_t stream_id, int32_t length, void *user_data)
00379 {
00380   SpdyClientSession *sm = (SpdyClientSession *)user_data;
00381   SpdyRequest *req = sm->find_request(stream_id);
00382 
00383   spdy_show_data_frame("++++RECV", session, flags, stream_id, length, user_data);
00384 
00385   
00386   
00387   
00388   
00389   
00390   if (!req) {
00391     TSVIOReenable(sm->write_vio);
00392     return;
00393   }
00394 
00395   req->delta_window_size += length;
00396 
00397   Debug("spdy", "----sm_id:%" PRId64 ", stream_id:%d, delta_window_size:%u",
00398         sm->sm_id, stream_id, req->delta_window_size);
00399 
00400   if (req->delta_window_size >= spdy_initial_window_size/2) {
00401     Debug("spdy", "----Reenable write_vio for WINDOW_UPDATE frame, delta_window_size:%u",
00402           req->delta_window_size);
00403 
00404     
00405     
00406     
00407     
00408     
00409     TSVIOReenable(sm->write_vio);
00410 
00411     req->delta_window_size = 0;
00412   }
00413 
00414   return;
00415 }
00416 
00417 void
00418 spdy_before_ctrl_send_callback(spdylay_session * ,
00419                                spdylay_frame_type ,
00420                                spdylay_frame * ,
00421                                void * )
00422 {
00423   
00424   return;
00425 }
00426 
00427 void
00428 spdy_on_ctrl_send_callback(spdylay_session *session, spdylay_frame_type type,
00429                            spdylay_frame *frame, void *user_data)
00430 {
00431   spdy_show_ctl_frame("----SEND", session, type, frame, user_data);
00432 
00433   return;
00434 }
00435 
00436 void
00437 spdy_on_ctrl_not_send_callback(spdylay_session * ,
00438                                spdylay_frame_type ,
00439                                spdylay_frame * ,
00440                                int ,
00441                                void * )
00442 {
00443   
00444   return;
00445 }
00446 
00447 void
00448 spdy_on_data_send_callback(spdylay_session *session, uint8_t flags,
00449                            int32_t stream_id, int32_t length, void *user_data)
00450 {
00451   SpdyClientSession *sm = (SpdyClientSession *)user_data;
00452 
00453   spdy_show_data_frame("----SEND", session, flags, stream_id, length, user_data);
00454 
00455   TSVIOReenable(sm->read_vio);
00456   return;
00457 }
00458 
00459 void
00460 spdy_on_stream_close_callback(spdylay_session * ,
00461                               int32_t ,
00462                               spdylay_status_code ,
00463                               void * )
00464 {
00465   
00466   return;
00467 }
00468 
00469 ssize_t
00470 spdy_get_credential_proof(spdylay_session * ,
00471                           const spdylay_origin * ,
00472                           uint8_t * ,
00473                           size_t ,
00474                           void * )
00475 {
00476   
00477   return 0;
00478 }
00479 
00480 ssize_t
00481 spdy_get_credential_ncerts(spdylay_session * ,
00482                            const spdylay_origin * ,
00483                            void * )
00484 {
00485   
00486   return 0;
00487 }
00488 
00489 ssize_t
00490 spdy_get_credential_cert(spdylay_session * ,
00491                          const spdylay_origin * ,
00492                          size_t ,
00493                          uint8_t * ,
00494                          size_t ,
00495                          void * )
00496 {
00497   
00498   return 0;
00499 }
00500 
00501 void
00502 spdy_on_request_recv_callback(spdylay_session * ,
00503                               int32_t ,
00504                               void * )
00505 {
00506   
00507   return;
00508 }
00509 
00510 void
00511 spdy_on_ctrl_recv_parse_error_callback(spdylay_session * ,
00512                                        spdylay_frame_type ,
00513                                        const uint8_t * ,
00514                                        size_t ,
00515                                        const uint8_t * ,
00516                                        size_t ,
00517                                        int ,
00518                                        void * )
00519 {
00520   
00521   return;
00522 }
00523 
00524 void
00525 spdy_on_unknown_ctrl_recv_callback(spdylay_session * ,
00526                                    const uint8_t * ,
00527                                    size_t ,
00528                                    const uint8_t * ,
00529                                    size_t ,
00530                                    void * )
00531 {
00532   
00533   return;
00534 }