• Main Page
  • Related Pages
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

SpdyCallbacks.cc

Go to the documentation of this file.
00001 /** @file
00002 
00003   SpdyCallbacks.cc
00004 
00005   @section license License
00006 
00007   Licensed to the Apache Software Foundation (ASF) under one
00008   or more contributor license agreements.  See the NOTICE file
00009   distributed with this work for additional information
00010   regarding copyright ownership.  The ASF licenses this file
00011   to you under the Apache License, Version 2.0 (the
00012   "License"); you may not use this file except in compliance
00013   with the License.  You may obtain a copy of the License at
00014 
00015       http://www.apache.org/licenses/LICENSE-2.0
00016 
00017   Unless required by applicable law or agreed to in writing, software
00018   distributed under the License is distributed on an "AS IS" BASIS,
00019   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00020   See the License for the specific language governing permissions and
00021   limitations under the License.
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 * /*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 * /*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   // HTTP content should be dechunked before packed into SPDY.
00184   //
00185   fetch_flags = TS_FETCH_FLAGS_DECHUNK;
00186 
00187   // TS-2906: FetchSM sets requests are internal requests, we need to not do that for SPDY streams.
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   // Set header list
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 * /*session*/, const uint8_t *data, size_t length,
00214                    int /*flags*/, 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 * /*session*/, uint8_t *buf, size_t length,
00228                    int /*flags*/, 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   // This is a bit of a hack. If we are reading out of the buffer the protocol probe acceptor gave us, then we have not
00260   // kicked off our own I/O yet. After consuming this data we will come back and do that.
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   // validate request headers
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 * /*session*/,
00347                                    spdylay_frame_type /*type*/,
00348                                    spdylay_frame * /*frame*/,
00349                                    uint32_t /*status_code*/,
00350                                    void * /*user_data*/)
00351 {
00352   //TODO
00353   return;
00354 }
00355 
00356 void
00357 spdy_on_data_chunk_recv_callback(spdylay_session * /*session*/, uint8_t /*flags*/,
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   // SpdyRequest has been deleted on error, drop this data;
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   // After SpdyRequest has been deleted on error, the corresponding
00387   // client might continue to send POST data, We should reenable
00388   // sm->write_vio so that WINDOW_UPDATE has a chance to be sent.
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     // Need not to send WINDOW_UPDATE frame here, what we should
00406     // do is to reenable sm->write_vio, and than spdylay_session_send()
00407     // will be triggered and it'll send WINDOW_UPDATE frame automatically.
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 * /*session*/,
00419                                spdylay_frame_type /*type*/,
00420                                spdylay_frame * /*frame*/,
00421                                void * /*user_data*/)
00422 {
00423   //TODO
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 * /*session*/,
00438                                spdylay_frame_type /*type*/,
00439                                spdylay_frame * /*frame*/,
00440                                int /*error_code*/,
00441                                void * /*user_data*/)
00442 {
00443   //TODO
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 * /*session*/,
00461                               int32_t /*stream_id*/,
00462                               spdylay_status_code /*status_code*/,
00463                               void * /*user_data*/)
00464 {
00465   //TODO
00466   return;
00467 }
00468 
00469 ssize_t
00470 spdy_get_credential_proof(spdylay_session * /*session*/,
00471                           const spdylay_origin * /*origin*/,
00472                           uint8_t * /*proof*/,
00473                           size_t /*prooflen*/,
00474                           void * /*user_data*/)
00475 {
00476   //TODO
00477   return 0;
00478 }
00479 
00480 ssize_t
00481 spdy_get_credential_ncerts(spdylay_session * /*session*/,
00482                            const spdylay_origin * /*origin*/,
00483                            void * /*user_data*/)
00484 {
00485   //TODO
00486   return 0;
00487 }
00488 
00489 ssize_t
00490 spdy_get_credential_cert(spdylay_session * /*session*/,
00491                          const spdylay_origin * /*origin*/,
00492                          size_t /*idx*/,
00493                          uint8_t * /*cert*/,
00494                          size_t /*certlen*/,
00495                          void * /*user_data*/)
00496 {
00497   //TODO
00498   return 0;
00499 }
00500 
00501 void
00502 spdy_on_request_recv_callback(spdylay_session * /*session*/,
00503                               int32_t /*stream_id*/,
00504                               void * /*user_data*/)
00505 {
00506   //TODO
00507   return;
00508 }
00509 
00510 void
00511 spdy_on_ctrl_recv_parse_error_callback(spdylay_session * /*session*/,
00512                                        spdylay_frame_type /*type*/,
00513                                        const uint8_t * /*head*/,
00514                                        size_t /*headlen*/,
00515                                        const uint8_t * /*payload*/,
00516                                        size_t /*payloadlen*/,
00517                                        int /*error_code*/,
00518                                        void * /*user_data*/)
00519 {
00520   //TODO
00521   return;
00522 }
00523 
00524 void
00525 spdy_on_unknown_ctrl_recv_callback(spdylay_session * /*session*/,
00526                                    const uint8_t * /*head*/,
00527                                    size_t /*headlen*/,
00528                                    const uint8_t * /*payload*/,
00529                                    size_t /*payloadlen*/,
00530                                    void * /*user_data*/)
00531 {
00532   //TODO
00533   return;
00534 }

Generated by  doxygen 1.7.1